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

此文為譯文,原文地址


介紹

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

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

我們可以想象下有這么一個(gè)網(wǎng)上商城,它由用戶(hù)、目錄、購(gòu)物車(chē)、訂單等多個(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)中的一小部分-用戶(hù)微服務(wù)。

Web應(yīng)用可以通過(guò)請(qǐng)求restful api訪(fǎng)問(wèn)用戶(hù)微服務(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ī)制,你可能還記得,它依賴(lài)于一個(gè)注冊(cè)中心,從而使RMI服務(wù)能夠找到對(duì)方。微服務(wù)也有同樣的需求。

Netflix的開(kāi)發(fā)人員設(shè)計(jì)并開(kāi)源了一套服務(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屬性來(lái)讓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ù),你可以通過(guò) http://localhost:1111來(lái)訪(fǎng)問(wèn)Eureka的主界面。


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

微服務(wù)是一個(gè)用來(lái)處理一個(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è)賬戶(hù)管理的微服務(wù)需要使用Spring Data AccountRepository來(lái)實(shí)現(xiàn)一個(gè)JPA,還需要使用Spring REST來(lái)提供RESTful接口顯示賬戶(hù)信息。這正好就是實(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) 引入配置類(lèi)

此外,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

說(shuō)明:
1.設(shè)置應(yīng)用名為accounts-service,注冊(cè)和訪(fǎng)問(wèn)都使用這個(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í)間。你可以訪(fǎng)問(wèn)http://localhost:1111/eureka/apps/來(lái)查詢(xún)更多的信息:

<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>

訪(fǎng)問(wèn)微服務(wù)

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

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

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

@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ù)

訪(fǎng)問(wèn)微服務(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)該寫(xiě)死 ,這里只是示例
         // 2. 大小寫(xiě)不敏感,也可以是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的類(lèi),在本例中,我們自己創(chuàng)建了WebAccountController,所以我取消了自動(dòng)掃描@ComponentScan(useDefaultFilters=false)

  3. service-url 需要與spring.application.name中一致,而不是真實(shí)的訪(fǎng)問(wèn)地址。如account-service.yml中的accounts-service。

RestTemplate 負(fù)載均衡

Spring Cloud會(huì)自動(dòng)配置RestTemplate使用Netflix的 Ribbon來(lái)實(shí)現(xiàn)HTTP客戶(hù)端。
當(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í)例是線(xiàn)程安全的,它可以訪(fǎng)問(wèn)任意數(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 {
...
}

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

  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類(lèi)。

  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)]說(shuō)明白的的,請(qǐng)下載并閱讀源代碼~~

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容