Spring Cloud入門與實踐(一)-Eureka
微服務,很火的一個概念,無數的架構師和開發者在實際專案中實踐該設計理念併為此付出了諸多努力,同時也湧現出針對不同應用場景出現的各種問題的各種解決方案和開源框架,
- 服務治理例如Dubbo,Netflix的Eureka。
- 分散式配置管理Spring Cloud的Config等。
- 批量任務例如Spring Cloud的Task,LinkedIn的Azkaban
- 服務跟蹤入京東的Hydra,Spring Cloud的SleuthDENG1
Spring Cloud的出現,可以說是對微服務框架的巨大支援和強有力的技術後盾,它不像前面框架所說的,只是解決微服務中的某一個問題,而是一個解決微服務架構實施的綜合性解決框架,它整合了諸多廣泛實踐和證明過的框架作為實施的基礎部件,又在該體系基礎上建立了一些非常優秀的邊緣元件。
Spring Cloud是一個基於Spring Boot實現的微服務架構開發工具,他為服務架構中設計的配置管理,服務治理,斷路器,智慧路由,微代理,控制匯流排,全域性鎖,決策競選,分散式會話等提供了叢集狀態等操作提供了一種簡單的開發方式。
Whta is Eureka
本篇文章主要介紹Spring Cloud Eureka,它基於Netflix Eureka做了二次封裝,主要完成微服務框架中服務治理功能。
Eureka是以伺服器/客戶端模式,客戶端(其他微服務系統)通過向Eureka註冊中心進行註冊。
例子
就基於Spring Boot的例子來說,Spring Cloud Eureka的Hello World是十分簡單的
Server端
- 首先配置pom檔案:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.13.RELEASE</version>
</parent>
<dependencies>
<dependency >
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Edgware.SR3</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
對於spring-boot-starter-test
,版本是通過parent
標籤來確定的。
- 編寫一個包含main方法的啟動類:
@SpringBootApplication
@EnableEurekaServer
public class EurekaApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaApplication.class, args);
}
}
通過@EnableEurekaServer註解啟動一個服務註冊中心,提供其他應用進行對話。
在預設配置下面,該服務註冊中心也會講自己作為客戶端來嘗試註冊自己,所以如果是單服務註冊中心,需要禁止自己註冊自己,
如果是多註冊中心,實現高可用,則需要相互註冊。
- application.yml配置
server:
port: 8761 #埠
eureka:
instance:
hostname: localhost #例項名稱
client:
registerWithEureka: false #該應用為註冊中心,這個代表不能自己註冊自己
fetchRegistry: false #註冊中心職責維護服務示例,不需要去檢索服務,設為false
server:
waitTimeInMsWhenSyncEmpty: 0 #立即檢索
成功進入之後,則可以看到如下頁面:
Client端
剛剛實現了一個Eureka服務註冊中心,現在來實現一個Spring Boot應用加入到Eureka的服務治理體系中去
1. pom檔案:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.13.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Edgware.SR3</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
具體pom依賴和eureka服務端類似,參考上面解釋!
- 實現一個Eureka的客戶端:
@EnableDiscoveryClient
@SpringBootApplication
public class HelloServiceApplication {
public static void main(String[] args){
SpringApplication.run(HelloServiceApplication.class,args);
}
}
- 實現一個業務Controller:
@RestController
public class HelloController {
private final Logger logger = Logger.getLogger(getClass());
@Autowired
private DiscoveryClient client;
@RequestMapping(value = "/hello",method = RequestMethod.GET)
public String index(){
ServiceInstance config = client.getInstances("localhost").get(0);
logger.info("/hello-service:"+config.getHost() +"--------service_id:"+config.getServiceId());
return "hello-service";
}
}
很簡單的一個RestController
,通過@Autowired
注入。在
ServiceInstance config = client.getInstances("localhost").get(0);
因為getLocalServiceInstance
已經被廢棄了,所以需要用getInstances
來獲取
往getInstances
中,輸入eureka Server
的instance name
,從而獲取所有例項(非公有)。
- application.yml配置如下:
spring:
application:
name: hello-service #為服務命名
eureka:
client:
serviceUrl:
#defaultZone: http://localhost:8761/eureka/ #指定開始的eureka server的註冊中心
defaultZone: http://${eureka.host:localhost}:${eureka.port:8761}/eureka/ #推薦這種方法
當Eureka Client啟動時,在Eureka Server會有一段日誌輸出,說明有新服務註冊進來:
2018-06-09 17:11:07.946 INFO 11568 --- [nio-8761-exec-8] c.n.e.registry.AbstractInstanceRegistry :
Registered instance HELLO-SERVICE/192.168.1.107:hello-service with status UP (replication=false)
另一方面,當重啟Eureka Client啟動時,相應也會有一段輸出,當然如果是直接重啟(沒有任何修改),則Eureka Client註冊狀態返回
為204。204代表伺服器返回的資訊為未修改,即沒有任何改變。
2018-06-09 17:11:07.948 INFO 14356 --- [nfoReplicator-0] com.netflix.discovery.DiscoveryClient :
DiscoveryClient_HELLO-SERVICE/192.168.1.107:hello-service - registration status: 204
此時看看Eureka Server的介面:
注意介面上有一串紅色文字:
EMERGENCY! EUREKA MAY BE INCORRECTLY CLAIMING INSTANCES ARE UP WHEN THEY'RE NOT.
RENEWALS ARE LESSER THAN THRESHOLD AND HENCE THE INSTANCES ARE NOT BEING EXPIRED JUST TO BE SAFE.
這個警告是因為觸發了Eureka Server的自我保護機制,因為服務註冊到Eureka Server之後,會維護一個心跳連線,告訴Eureka Server自己還活著。Eureka Server在執行期間,會統計心跳失敗的比例在15分鐘內,是否低於85%。低於就會報警。
因為一旦出現這種問題,那麼客戶端很容易拿到實際已經不存在的服務示例,會出現呼叫失敗的情況,所以客戶端必須有容錯機制,比如可以使用請求,斷路器等機制。
另外可以在application.yml
中增加eureka.server.enable-self-preservation=false
來關閉保護機制。
一個簡單的Eureka Server和Eureka Client已完成。
下面將使用採用一體化將其釋出到docker中。
Docker 中執行
利用docker-maven-plugin
外掛,來實現對Spring Boot專案的打包
eureka-server
首先專案目錄為:
在目錄eureka-server的pom檔案增加外掛:
<plugin>
<groupId>com.spotify</groupId>
<artifactId>docker-maven-plugin</artifactId>
<configuration>
<imageName>${project.name}:${project.version}</imageName>
<dockerDirectory>${project.basedir}/src/main/docker</dockerDirectory>
<skipDockerBuild>false</skipDockerBuild>
<resources>
<resource>
<directory>${project.build.directory}</directory>
<include>${project.build.finalName}.jar</include>
</resource>
</resources>
</configuration>
</plugin>
由上面可知,<dockerDirectory>
配置了Dockerfile配置,即在Java同級目錄增加一個docker
資料夾,並在裡面編寫Dockerfile
:
FROM java:8
VOLUME /tmp/docker
RUN mkdir /app
COPY eureka-server-1.0-SNAPSHOT.jar /app/app.jar
COPY runboot.sh /app/
RUN bash -c 'touch /app/app.jar'
WORKDIR /app
RUN chmod a+x runboot.sh
EXPOSE 8761
CMD /app/runboot.sh
另外runboot.sh:
java -Djava.security.egd=file:/dev/./urandom -jar /app/app.jar
關於-Djava.security.egd=file:/dev/./urandom
主要目的就是來生成一個隨機數,關於隨機數生成時,採用的“熵源”(entropy source)的策略。
參看:http://ifeve.com/jvm-random-and-entropy-source/
hello-service
在hello-service中的配置與eureka-server類似,這裡就不贅述
docker-compose
docker-compose是docker三架馬車之一,採用python編寫,可以理解為把一些Dockerfile裡面命令,整合到一起。比如我要啟動一個Eureka伺服器,然後啟動一個客戶端,此時需要用docker啟動Eureka-server,然後在用docker啟動hello-service,此時要執行兩遍命令。
採用docker-compose,我們只需要執行docker-compose up
執行就可以了,例如本專案中:
version: "1"
services:
eureka-server:
image: "eureka-server:1.0-SNAPSHOT"
hostname: eureka-server
container_name: eureka-server
ports:
- "8761:8761"
hello-service:
image: "hello-service:1.0-SNAPSHOT"
hostname: hello-service
container_name: hello-service
links:
- eureka-server #連結到另一個容器
environment:
EUREKA_HOST: eureka-server
EUREKA_PORT: 8761
具體程式碼檔案上傳到了csdn,需要的同學可自行下載,然後按以下步驟執行:
https://download.csdn.net/download/anla_/10469436
1. mvn clean package docker:build
2. docker-compose up