[譯]Spring構建微服務

二胡嘈子發表於2016-05-20

此文為譯文,原文地址


介紹

本文通過一個使用Spring、Spring Boot和Spring Cloud的小例子來說明如何構建微服務系統。

我們可以通過數個微服務組合成一個大型系統。

我們可以想象下有這麼一個網上商城,它由使用者、目錄、購物車、訂單等多個獨立的為服務組成。
[譯]Spring構建微服務

這裡難免需要安裝和配置不少元件才能構建這樣一個系統。為了讓它們更好的合作,你需要熟悉Spring Boot、Spring Cloud。

本文的目標很明確,就是一步一步構建一個最簡單的系統。因此,這裡只會實現系統中的一小部分-使用者微服務。

Web應用可以通過請求restful api訪問使用者微服務。這裡也會包含發現服務-讓其他服務可以知道彼此。

[譯]Spring構建微服務


其他資源

本文只是討論了一個最簡單的系統,更多的內容,你可以閱讀Josh Long的部落格

服務註冊

當你有多個服務協同工作時,它們需要互相彼此知道。如果你之前瞭解java RMI機制,你可能還記得,它依賴於一個註冊中心,從而使RMI服務能夠找到對方。微服務也有同樣的需求。

Netflix的開發人員設計並開源了一套服務註冊系統,叫做Eureka。目前這套系統已被合併進了Spring Cloud,我們可以很容易的執行一個Eureka服務。例如:

@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);
  }
}

就是這麼簡單。

POM中的核心內容如下:

    <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的預設配置可以檢視application.properties或者application.yml檔案。當你有多個Spring Boot應用的時候,你可以配置spring.config.name屬性來讓Spring Boot查詢不同的配置檔案。

此應用還需配置registration-server.propertiesregistration-server.yml檔案。以下是registration-server.yml中的相關配置:

# Configure this Discovery Server
eureka:
  instance:
    hostname: localhost
  client:  # 只註冊服務端
    registerWithEureka: false
    fetchRegistry: false

server:
  port: 1111   # HTTP (Tomcat) port

Eureka預設執行在8761埠,這裡我們把它修改為1111埠。配置中制定了這裡是服務端,並且阻止註冊自身服務。

現在執行我們的註冊服務,你可以通過 http://localhost:1111來訪問Eureka的主介面。


建立微服務:使用者服務

微服務是一個用來處理一個明確需求的獨立元件。

我們總是在強調要構建高內聚,低耦合的架構,這已經是老生常談了。但是,這裡我們不是在元件(Spring Beans)級別實現,而是在介面之間實現。

[譯]Spring構建微服務

例如,我有一個賬戶管理的微服務需要使用Spring Data AccountRepository來實現一個JPA,還需要使用Spring REST來提供RESTful介面顯示賬戶資訊。這正好就是實現了一個簡單的spring boot應用。

我們如何讓它被註冊到註冊服務中:

@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 - 定義了這是一個Spring Boot應用
  2. @EnableDiscoveryClient - 執行服務被註冊到註冊服務中
  3. @Import(AccountsWebApplication.class) 引入配置類

此外,YML配置檔案內容如下:

# 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.設定應用名為accounts-service,註冊和訪問都使用這個名字
2.服務釋出在2222埠
3.配置Eureka 服務URL

現在執行服務然後重新整理http://localhost:1111 ,你會看到ACCOUNTS-SERVICE顯示在應用列表中。

[譯]Spring構建微服務

有時候,註冊服務需要用10到20秒時間。你可以訪問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>

訪問微服務

Spring提供了RestTemplate類來訪問RESTful類。它可以讓你傳送HTTP請求至RESTful服務並且接收和處理不同型別的響應資料-包括JSON和XML。

封裝微服務呼叫

在客戶端應用裡有一個WebAccountService類:

@Service
public class WebAccountsService {

    @Autowired        //  Spring Cloud 自動注入
    @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從微服務中獲取資料

訪問微服務

WebAccountController設定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. 不應該寫死 ,這裡只是示例
         // 2. 大小寫不敏感,也可以是http://accounts-service
         return new WebAccountsController
                       ("http://ACCOUNTS-SERVICE");  // serviceUrl
    }
}

以下幾點需要注意:

  1. WebController是一個典型的Spring MVC控制器,此應用使用thymeleaf作為檢視引擎。

  2. Spring Boot會預設掃描註解了@Component的類,在本例中,我們自己建立了WebAccountController,所以我取消了自動掃描@ComponentScan(useDefaultFilters=false)

  3. service-url 需要與spring.application.name中一致,而不是真實的訪問地址。如account-service.yml中的accounts-service。

RestTemplate 負載均衡

Spring Cloud會自動配置RestTemplate使用Netflix的 Ribbon來實現HTTP客戶端。
當你的某個服務存在多個例項是,Ribbon會使用自動選擇其中的一個。

如果你檢視RibbonClientHttpRequestFactory的原始碼,你會發現:

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

RestTemplate例項是執行緒安全的,它可以訪問任意數量的應用程式中的不同服務。

配置

現在我們配置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 {
...
}

這是賬戶服務的配置類,其中註解的含義如下:

  1. @SpringBootApplication - 定義了這是一個Sping Boot應用。這個註解等同於@EnableAutoConfiguration
    , @Configuration和 @ComponentScan(預設情況Spring會掃描當前包和子包中的所有可能的beans,如AccountController
     和AccountRepository)

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

  3. @EnableJpaRepositories("io.pivotal.microservices.accounts") - 搜尋Repository介面並使用JPA自動實現。(Spring Data JPA
     
  4. @PropertySource("classpath:db-config.properties")- 配置資料來源屬性


至此,我們已經實現了一個簡單的微服務示例,如果什麼地方沒說明白的的,請下載並閱讀原始碼~~

各位看官,請打賞。

相關文章