此文為譯文,原文地址
介紹
本文通過一個使用Spring、Spring Boot和Spring Cloud的小例子來說明如何構建微服務系統。
我們可以通過數個微服務組合成一個大型系統。
我們可以想象下有這麼一個網上商城,它由使用者、目錄、購物車、訂單等多個獨立的為服務組成。
這裡難免需要安裝和配置不少元件才能構建這樣一個系統。為了讓它們更好的合作,你需要熟悉Spring Boot、Spring Cloud。
本文的目標很明確,就是一步一步構建一個最簡單的系統。因此,這裡只會實現系統中的一小部分-使用者微服務。
Web應用可以通過請求restful api訪問使用者微服務。這裡也會包含發現服務-讓其他服務可以知道彼此。
其他資源
本文只是討論了一個最簡單的系統,更多的內容,你可以閱讀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.properties
或registration-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 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);
}
}
答案是註解:
@EnableAutoConfiguration
- 定義了這是一個Spring Boot應用@EnableDiscoveryClient
- 執行服務被註冊到註冊服務中@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顯示在應用列表中。
有時候,註冊服務需要用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
}
}
以下幾點需要注意:
WebController
是一個典型的Spring MVC控制器,此應用使用thymeleaf作為檢視引擎。Spring Boot會預設掃描註解了
@Component
的類,在本例中,我們自己建立了WebAccountController
,所以我取消了自動掃描@ComponentScan(useDefaultFilters=false)
。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 {
...
}
這是賬戶服務的配置類,其中註解的含義如下:
@SpringBootApplication - 定義了這是一個Sping Boot應用。這個註解等同於@EnableAutoConfiguration
, @Configuration和 @ComponentScan(預設情況Spring會掃描當前包和子包中的所有可能的beans,如AccountController
和AccountRepository)@EntityScan("io.pivotal.microservices.accounts") - 這裡用了JPA,所有我需要用到@Entity類。
- @EnableJpaRepositories("io.pivotal.microservices.accounts") - 搜尋Repository介面並使用JPA自動實現。(Spring Data JPA)
@PropertySource("classpath:db-config.properties")- 配置資料來源屬性
至此,我們已經實現了一個簡單的微服務示例,如果什麼地方沒說明白的的,請下載並閱讀原始碼~~
各位看官,請打賞。