此文為譯文杖狼,原文地址
介紹
本文通過一個(gè)使用Spring、Spring Boot和Spring Cloud的小例子來說明如何構(gòu)建微服務(wù)系統(tǒng)妖爷。
我們可以通過數(shù)個(gè)微服務(wù)組合成一個(gè)大型系統(tǒng)蝶涩。
我們可以想象下有這么一個(gè)網(wǎng)上商城,它由用戶絮识、目錄绿聘、購物車、訂單等多個(gè)獨(dú)立的為服務(wù)組成次舌。
這里難免需要安裝和配置不少組件才能構(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ù)可以知道彼此棚赔。
本文實(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.properties
或registration-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)。
例如粗井,我有一個(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);
}
}
答案是注解:
-
@EnableAutoConfiguration
- 定義了這是一個(gè)Spring Boot應(yīng)用 -
@EnableDiscoveryClient
- 運(yùn)行服務(wù)被注冊(cè)到注冊(cè)服務(wù)中 -
@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)用列表中耘擂。
有時(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)需要注意:
WebController
是一個(gè)典型的Spring MVC控制器铃绒,此應(yīng)用使用thymeleaf作為視圖引擎。Spring Boot會(huì)默認(rèn)掃描注解了
@Component
的類螺捐,在本例中颠悬,我們自己創(chuàng)建了WebAccountController
,所以我取消了自動(dòng)掃描@ComponentScan(useDefaultFilters=false)
矮燎。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ù)的配置類,其中注解的含義如下:
@SpringBootApplication - 定義了這是一個(gè)Sping Boot應(yīng)用正什。這個(gè)注解等同于@EnableAutoConfiguration
, @Configuration和 @ComponentScan(默認(rèn)情況Spring會(huì)掃描當(dāng)前包和子包中的所有可能的beans,如AccountController
和AccountRepository)@EntityScan("io.pivotal.microservices.accounts") - 這里用了JPA贤壁,所有我需要用到@Entity類。
@EnableJpaRepositories("io.pivotal.microservices.accounts") - 搜索Repository接口并使用JPA自動(dòng)實(shí)現(xiàn)埠忘。(Spring Data JPA)
@PropertySource("classpath:db-config.properties")- 配置數(shù)據(jù)源屬性
至此脾拆,我們已經(jīng)實(shí)現(xiàn)了一個(gè)簡(jiǎn)單的微服務(wù)示例,如果什么地方?jīng)]說明白的的莹妒,請(qǐng)下載并閱讀源代碼~~
各位看官名船,請(qǐng)打賞。