[譯]Spring構(gòu)建微服務(wù)

此文為譯文杖狼,原文地址


介紹

本文通過一個(gè)使用Spring、Spring Boot和Spring Cloud的小例子來說明如何構(gòu)建微服務(wù)系統(tǒng)妖爷。

我們可以通過數(shù)個(gè)微服務(wù)組合成一個(gè)大型系統(tǒng)蝶涩。

我們可以想象下有這么一個(gè)網(wǎng)上商城,它由用戶絮识、目錄绿聘、購物車、訂單等多個(gè)獨(dú)立的為服務(wù)組成次舌。


shopping-system.jpg

這里難免需要安裝和配置不少組件才能構(gòu)建這樣一個(gè)系統(tǒng)熄攘。為了讓它們更好的合作,你需要熟悉Spring Boot彼念、Spring Cloud挪圾。

本文的目標(biāo)很明確,就是一步一步構(gòu)建一個(gè)最簡(jiǎn)單的系統(tǒng)逐沙。因此哲思,這里只會(huì)實(shí)現(xiàn)系統(tǒng)中的一小部分-用戶微服務(wù)。

Web應(yīng)用可以通過請(qǐng)求restful api訪問用戶微服務(wù)吩案。這里也會(huì)包含發(fā)現(xiàn)服務(wù)-讓其他服務(wù)可以知道彼此棚赔。

mini-system.jpg

本文實(shí)例代碼


其他資源

本文只是討論了一個(gè)最簡(jiǎn)單的系統(tǒng),更多的內(nèi)容徘郭,你可以閱讀Josh Long的博客

服務(wù)注冊(cè)

當(dāng)你有多個(gè)服務(wù)協(xié)同工作時(shí)靠益,它們需要互相彼此知道。如果你之前了解java RMI機(jī)制崎岂,你可能還記得捆毫,它依賴于一個(gè)注冊(cè)中心闪湾,從而使RMI服務(wù)能夠找到對(duì)方冲甘。微服務(wù)也有同樣的需求。

Netflix的開發(fā)人員設(shè)計(jì)并開源了一套服務(wù)注冊(cè)系統(tǒng),叫做Eureka江醇。目前這套系統(tǒng)已被合并進(jìn)了Spring Cloud濒憋,我們可以很容易的運(yùn)行一個(gè)Eureka服務(wù)。例如:

@SpringBootApplication
@EnableEurekaServer
public class ServiceRegistrationServer {

  public static void main(String[] args) {
    //  配置文件 registration-server.yml
    System.setProperty("spring.config.name", "registration-server");
    SpringApplication.run(ServiceRegistrationServer.class, args);
  }
}

就是這么簡(jiǎn)單陶夜。

POM中的核心內(nèi)容如下:

    <parent>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-parent</artifactId>
        <version>Angel.SR3</version>  <!-- Name of release train -->
    </parent>
    <dependencies>
        <dependency>
            <!-- Setup Spring Boot -->
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <!-- Setup Spring MVC & REST, use Embedded Tomcat -->
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <!-- Spring Cloud starter -->
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter</artifactId>
        </dependency>

        <dependency>
            <!-- Eureka for service registration -->
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka-server</artifactId>
        </dependency>
    </dependencies>

Spring Boot的默認(rèn)配置可以查看application.properties或者application.yml文件凛驮。當(dāng)你有多個(gè)Spring Boot應(yīng)用的時(shí)候,你可以配置spring.config.name屬性來讓Spring Boot查找不同的配置文件条辟。

此應(yīng)用還需配置registration-server.propertiesregistration-server.yml文件黔夭。以下是registration-server.yml中的相關(guān)配置:

# Configure this Discovery Server
eureka:
  instance:
    hostname: localhost
  client:  # 只注冊(cè)服務(wù)端
    registerWithEureka: false
    fetchRegistry: false

server:
  port: 1111   # HTTP (Tomcat) port

Eureka默認(rèn)運(yùn)行在8761端口,這里我們把它修改為1111端口羽嫡。配置中制定了這里是服務(wù)端本姥,并且阻止注冊(cè)自身服務(wù)。

現(xiàn)在運(yùn)行我們的注冊(cè)服務(wù)杭棵,你可以通過 http://localhost:1111來訪問Eureka的主界面婚惫。


創(chuàng)建微服務(wù):用戶服務(wù)

微服務(wù)是一個(gè)用來處理一個(gè)明確需求的獨(dú)立組件。

我們總是在強(qiáng)調(diào)要構(gòu)建高內(nèi)聚魂爪,低耦合的架構(gòu)先舷,這已經(jīng)是老生常談了。但是滓侍,這里我們不是在組件(Spring Beans)級(jí)別實(shí)現(xiàn)蒋川,而是在接口之間實(shí)現(xiàn)。

beans-vs-processes.jpg

例如粗井,我有一個(gè)賬戶管理的微服務(wù)需要使用Spring Data AccountRepository來實(shí)現(xiàn)一個(gè)JPA,還需要使用Spring REST來提供RESTful接口顯示賬戶信息尔破。這正好就是實(shí)現(xiàn)了一個(gè)簡(jiǎn)單的spring boot應(yīng)用。

我們?nèi)绾巫屗蛔?cè)到注冊(cè)服務(wù)中:

@EnableAutoConfiguration
@EnableDiscoveryClient
@Import(AccountsWebApplication.class)
public class AccountsServer {

    @Autowired
    AccountRepository accountRepository;

    public static void main(String[] args) {
        // 配置文件 accounts-server.yml
        System.setProperty("spring.config.name", "accounts-server");

        SpringApplication.run(AccountsServer.class, args);
    }
}

答案是注解:

  1. @EnableAutoConfiguration - 定義了這是一個(gè)Spring Boot應(yīng)用
  2. @EnableDiscoveryClient - 運(yùn)行服務(wù)被注冊(cè)到注冊(cè)服務(wù)中
  3. @Import(AccountsWebApplication.class) 引入配置類

此外浇衬,YML配置文件內(nèi)容如下:

# Spring properties
spring:
  application:
     name: accounts-service

# Discovery Server Access
eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:1111/eureka/

# HTTP Server
server:
  port: 2222   # HTTP (Tomcat) port

說明:
1.設(shè)置應(yīng)用名為accounts-service懒构,注冊(cè)和訪問都使用這個(gè)名字
2.服務(wù)發(fā)布在2222端口
3.配置Eureka 服務(wù)URL

現(xiàn)在運(yùn)行服務(wù)然后刷新http://localhost:1111 ,你會(huì)看到ACCOUNTS-SERVICE顯示在應(yīng)用列表中耘擂。

dashboard.png

有時(shí)候胆剧,注冊(cè)服務(wù)需要用10到20秒時(shí)間。你可以訪問http://localhost:1111/eureka/apps/來查詢更多的信息:

<applications>
    <versions__delta>1</versions__delta>
    <apps__hashcode>UP_1_</apps__hashcode>
    <application>
        <name>ACCOUNTS-SERVICE</name>
        <instance>
            <hostName>autgchapmp1m1.corp.emc.com</hostName>
            <app>ACCOUNTS-SERVICE</app>
            <ipAddr>172.16.84.1</ipAddr><status>UP</status>
            <overriddenstatus>UNKNOWN</overriddenstatus>
            <port enabled="true">3344</port>
            <securePort enabled="false">443</securePort>
            ...
        </instance>
    </application>
</applications>

訪問微服務(wù)

Spring提供了RestTemplate類來訪問RESTful類醉冤。它可以讓你發(fā)送HTTP請(qǐng)求至RESTful服務(wù)并且接收和處理不同類型的響應(yīng)數(shù)據(jù)-包括JSON和XML秩霍。

封裝微服務(wù)調(diào)用

在客戶端應(yīng)用里有一個(gè)WebAccountService類:

@Service
public class WebAccountsService {

    @Autowired        //  Spring Cloud 自動(dòng)注入
    @LoadBalanced
    protected RestTemplate restTemplate; 

    protected String serviceUrl;

    public WebAccountsService(String serviceUrl) {
        this.serviceUrl = serviceUrl.startsWith("http") ?
               serviceUrl : "http://" + serviceUrl;
    }

    public Account getByNumber(String accountNumber) {
        Account account = restTemplate.getForObject(serviceUrl
                + "/accounts/{number}", Account.class, accountNumber);

        if (account == null)
            throw new AccountNotFoundException(accountNumber);
        else
            return account;
    }
    ...
}

WebAccountService使用RestTemplate從微服務(wù)中獲取數(shù)據(jù)

訪問微服務(wù)

WebAccountController設(shè)置serviceUrl

@SpringBootApplication
@EnableDiscoveryClient
@ComponentScan(useDefaultFilters=false)   
public class WebServer {

    public static void main(String[] args) {
        // Will configure using web-server.yml
        System.setProperty("spring.config.name", "web-server");
        SpringApplication.run(WebServer.class, args);
    }

    @Bean
    public WebAccountsController accountsController() {
         // 1. 不應(yīng)該寫死 ,這里只是示例
         // 2. 大小寫不敏感蚁阳,也可以是http://accounts-service
         return new WebAccountsController
                       ("http://ACCOUNTS-SERVICE");  // serviceUrl
    }
}

以下幾點(diǎn)需要注意:

  1. WebController是一個(gè)典型的Spring MVC控制器铃绒,此應(yīng)用使用thymeleaf作為視圖引擎。

  2. Spring Boot會(huì)默認(rèn)掃描注解了@Component的類螺捐,在本例中颠悬,我們自己創(chuàng)建了WebAccountController,所以我取消了自動(dòng)掃描@ComponentScan(useDefaultFilters=false)矮燎。

  3. service-url 需要與spring.application.name中一致,而不是真實(shí)的訪問地址赔癌。如account-service.yml中的accounts-service诞外。

RestTemplate 負(fù)載均衡

Spring Cloud會(huì)自動(dòng)配置RestTemplate使用Netflix的 Ribbon來實(shí)現(xiàn)HTTP客戶端。
當(dāng)你的某個(gè)服務(wù)存在多個(gè)實(shí)例是灾票,Ribbon會(huì)使用自動(dòng)選擇其中的一個(gè)峡谊。

如果你查看RibbonClientHttpRequestFactory的源碼,你會(huì)發(fā)現(xiàn):

String serviceId = originalUri.getHost();
ServiceInstance instance =
            loadBalancer.choose(serviceId);  // 負(fù)載均衡
... if instance non-null (service exists) ...
URI uri = loadBalancer.reconstructURI(instance, originalUri);

RestTemplate實(shí)例是線程安全的刊苍,它可以訪問任意數(shù)量的應(yīng)用程序中的不同服務(wù)既们。

配置

現(xiàn)在我們配置web-server.yml:

# Spring Properties
spring:
  application:
     name: web-service

# Discovery Server Access
eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:1111/eureka/

# HTTP Server
server:
  port: 3333   # HTTP (Tomcat) port

AccountsWebApplication 配置

@SpringBootApplication
@EntityScan("io.pivotal.microservices.accounts")
@EnableJpaRepositories("io.pivotal.microservices.accounts")
@PropertySource("classpath:db-config.properties")
public class AccountsWebApplication {
...
}

這是賬戶服務(wù)的配置類,其中注解的含義如下:

  1. @SpringBootApplication - 定義了這是一個(gè)Sping Boot應(yīng)用正什。這個(gè)注解等同于@EnableAutoConfiguration
    , @Configuration和 @ComponentScan(默認(rèn)情況Spring會(huì)掃描當(dāng)前包和子包中的所有可能的beans,如AccountController
    和AccountRepository)

  2. @EntityScan("io.pivotal.microservices.accounts") - 這里用了JPA贤壁,所有我需要用到@Entity類。

  3. @EnableJpaRepositories("io.pivotal.microservices.accounts") - 搜索Repository接口并使用JPA自動(dòng)實(shí)現(xiàn)埠忘。(Spring Data JPA

  4. @PropertySource("classpath:db-config.properties")- 配置數(shù)據(jù)源屬性


至此脾拆,我們已經(jīng)實(shí)現(xiàn)了一個(gè)簡(jiǎn)單的微服務(wù)示例,如果什么地方?jīng)]說明白的的莹妒,請(qǐng)下載并閱讀源代碼~~

各位看官名船,請(qǐng)打賞。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末旨怠,一起剝皮案震驚了整個(gè)濱河市渠驼,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌鉴腻,老刑警劉巖迷扇,帶你破解...
    沈念sama閱讀 216,919評(píng)論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異爽哎,居然都是意外死亡蜓席,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,567評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門课锌,熙熙樓的掌柜王于貴愁眉苦臉地迎上來厨内,“玉大人,你說我怎么就攤上這事渺贤〕福” “怎么了?”我有些...
    開封第一講書人閱讀 163,316評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵志鞍,是天一觀的道長(zhǎng)瞭亮。 經(jīng)常有香客問我,道長(zhǎng)固棚,這世上最難降的妖魔是什么统翩? 我笑而不...
    開封第一講書人閱讀 58,294評(píng)論 1 292
  • 正文 為了忘掉前任兼丰,我火速辦了婚禮,結(jié)果婚禮上唆缴,老公的妹妹穿的比我還像新娘。我一直安慰自己黍翎,他們只是感情好面徽,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,318評(píng)論 6 390
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著匣掸,像睡著了一般趟紊。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上碰酝,一...
    開封第一講書人閱讀 51,245評(píng)論 1 299
  • 那天霎匈,我揣著相機(jī)與錄音,去河邊找鬼送爸。 笑死铛嘱,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的袭厂。 我是一名探鬼主播墨吓,決...
    沈念sama閱讀 40,120評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼纹磺!你這毒婦竟也來了帖烘?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,964評(píng)論 0 275
  • 序言:老撾萬榮一對(duì)情侶失蹤橄杨,失蹤者是張志新(化名)和其女友劉穎秘症,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體式矫,經(jīng)...
    沈念sama閱讀 45,376評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡乡摹,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,592評(píng)論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了采转。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片趟卸。...
    茶點(diǎn)故事閱讀 39,764評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖氏义,靈堂內(nèi)的尸體忽然破棺而出锄列,到底是詐尸還是另有隱情,我是刑警寧澤惯悠,帶...
    沈念sama閱讀 35,460評(píng)論 5 344
  • 正文 年R本政府宣布邻邮,位于F島的核電站,受9級(jí)特大地震影響克婶,放射性物質(zhì)發(fā)生泄漏筒严。R本人自食惡果不足惜丹泉,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,070評(píng)論 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望鸭蛙。 院中可真熱鬧摹恨,春花似錦、人聲如沸娶视。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,697評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽肪获。三九已至寝凌,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間孝赫,已是汗流浹背较木。 一陣腳步聲響...
    開封第一講書人閱讀 32,846評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留青柄,地道東北人伐债。 一個(gè)月前我還...
    沈念sama閱讀 47,819評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像致开,于是被迫代替她去往敵國(guó)和親泳赋。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,665評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容