基於springcloud構建一個web專案
日子還很長,技術沉澱得一步步的來。先會用,然後再看實現原理
本篇部落格有點長,個人覺得還是比較細緻,希望對入手spring cloud的朋友能有所幫助
本來一直都想實踐一下zookeeper的,但是看了一篇關於CAP的討論之後,我還是選擇Eureka作為服務發現與服務治理的軟體。一個微服務專案需要的基礎元件有Eureka/Config/Ribbon/Hystrix/Zuul和訊息佇列。不過ribbon和hystrix提供的服務可以由Feign代替。權衡之下,還是先使用ribbon加hystrix,弄明白其中的原理,在之後的專案中再去實踐Feign。
服務註冊中心搭建
用idea ,利用spring 官方提供的 spring initialzar 建立一個包含 EurekaServer 元件的spring boot專案。
然後在主程式中加入
的註解,表示 啟用 Eureka註冊中心模組,本spring boot提供註冊中心的服務。然後在配置中寫入一下配置:
# 服務的埠號
server.port=8500
# 服務名稱:在服務發現中,服務名稱是一個關鍵資訊,如果同一個服務名稱有多個例項,那麼,消費者在呼叫的時候就可以使用ribbon提供的負載均衡來呼叫
spring.application.name=server-register
# 是否向服務中心註冊自己
eureka.client.register-with-eureka=false
# 是否需要檢索服務
eureka.client.fetch-registry=false
eureka.instance.hostname=localhost
# web端訪問地址
eureka.client.serviceUrl.defaultZone=http://${eureka.instance.hostname}:${server.port}/eureka/
然後啟動專案,訪問 serviceUrl得到如下介面,現在裡面的服務是空的,因為我們目前還沒有向裡面註冊任何服務
根據我自己的專案需求,我建立了7個服務,商城服務、使用者服務、資料庫服務、商品推薦服務、交易服務、快取服務、評價服務等 ,可能還會有訊息佇列的服務,資料庫服務可能需要氛圍 mysql的和 cockroachDB這兩種,cockroachDB是分散式的,用於儲存使用者的瀏覽記錄等,主要做後面的推薦做資料支撐。把建立好的服務全部定義到服務中心上面。所以再用 spring initialzar的方式建立七個服務,建立好之後,在每一個服務的主程式上面,加上註解 。表示啟用 Eureka客戶端模組。目前spring cloud無法直接支援 cockroachdb,所以建一個spring boot的預設專案,然後構建專案就可以了。在每個客戶端的配置檔案中,設定如下:
spring.application.name=wupingtuijian-service
eureka.client.serviceUrl.defaultZone=http://localhost:8500/eureka/
server.port=8608
配置完成之後,首先開啟 服務中心的服務,然後逐步開啟其他服務。可以在服務中心的日誌檔案中檢視到如下資訊
這就是其他服務向服務中心註冊的情況,開啟web端管理介面
現在,我們來將 Eureka單機 -> Eureka 叢集,實現高可用性。
利用 spring.profiles.active 屬性 來構建叢集,idea可以同時啟動多個相同的服務,只需要在edit服務中取消單例即可,如圖
具體的做法是,新建兩個配置檔案,命名為 application-xxx.properties的格式,
分別填入
server.port=8501
spring.application.name=server-register
#eureka.client.register-with-eureka=false
#eureka.client.fetch-registry=false
eureka.instance.hostname=peer1
eureka.client.serviceUrl.defaultZone=http://peer2:8502/eureka/,http://localhost:8500/eureka/
反正和原配置一樣,差不多改改埠之類的就行。
然後在 application.properties中設定 spring.propety.active 屬性就可以了,如果不設定,預設載入 application.properties ,如果設定了,將開啟設定的配置檔案。於是,我建立了三個 Eureka服務,如下:
然後現在修改其他所有服務的配置項,使之能夠註冊到註冊中心叢集
eureka.client.serviceUrl.defaultZone=http://localhost:8500/eureka/,http://peer1:8501/eureka/,http://peer2:8502/eureka/
開啟全部專案之後的效果如下:
服務註冊中心搭好了。經過上面的配置,可以感受到改變一個地方,就需要改變大量的配置檔案,為了避免這種不方便的方式,引入 spring-cloud 的 Config 服務。
引入配置服務
用 spring initialzar建立一個具有 configserver元件和 EurekaServer元件的spring boot專案,在主程式中加入 和 這兩個註釋分別表示啟用服務註冊客戶端元件和啟用配置服務元件。為了本地開發需要,我不使用預設的git配置的方式,而採取本地檔案系統的方式。具體的例子如下:
@EnableDiscoveryClient
@EnableConfigServer
@SpringBootApplication
public class ConfigmanagerApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigmanagerApplication.class, args);
}
}
application.properties
server.port=8400
spring.application.name=configmanager
eureka.client.serviceUrl.defaultZone=http://localhost:8500/eureka/,http://peer1:8501/eureka/,http://peer2:8502/eureka/
# 用 spring.profiles.active=native指明是用native的方式來構建的config的
spring.profiles.active=native
# 本地config的地址
spring.cloud.config.server.native.search-locations=D:/workspace/xinzu/nativeproperties
然後在其他服務中新增配置檔案 bootstrap.properties
spring.application.name=user-service
server.port=8607
spring.cloud.config.uri=http://localhost:8400/
spring.cloud.config.label=master
spring.cloud.config.profile=dev
eureka.client.serviceUrl.defaultZone=http://localhost:8500/eureka/,http://peer1:8501/eureka/,http://peer2:8502/eureka/
對配置檔案有特殊的命名要求,以我的模組為例子,應該在 D:/workspace/xinzu/nativeproperties 下面建立一個名為:
user-service-dev.properties 的配置檔案,dev是標誌,用 spring.cloud.config,profile 進行標記的。
在檔案裡面輸入以下測試配置
nickName=minqixing
然後在 user專案中建立controller,來查詢這個 nickName。專案程式碼如下
先來看看結構
config裡面定義的swagger2的啟動配置,關於swagger可以關注我的其他文章。
//UserApplication.class
@ServletComponentScan
@Configuration
@EnableAutoConfiguration
@EnableDiscoveryClient
@ComponentScan({"com.xinzu.user"})
public class UserApplication {
public static void main(String[] args) {
SpringApplication.run(UserApplication.class, args);
}
}
//TestController.class
@Api(value = "測試分散式環境是否可用", tags = {"測試springcloud環境"})
@Controller
@RefreshScope
public class TestController {
@Value("${nickName}")
private String nickName;
@ApiOperation(value = "測試 springcloud config", notes = "測試 springcloud config")
@RequestMapping(value = Path.TESTCONFIG, method = RequestMethod.GET)
@ResponseBody
public TestConfigDto getNickName(){
TestConfigDto dto = new TestConfigDto();
dto.setCode(200);
dto.setInfo(nickName);
return dto;
}
}
// TestConfigDto.class
public class TestConfigDto {
private String info;
private int code;
public int getCode() {
return code;
}
public String getInfo() {
return info;
}
public void setCode(int code) {
this.code = code;
}
public void setInfo(String info) {
this.info = info;
}
}
題外話
spring boot有個很好的特性,省去了很多配置,我個人感覺可以讓全民程式設計成為可能,哈哈。學會只要學會各種註釋的使用方法就可以了。置於實現原理,才是真正的程式設計人員需要了解的。所以,我個人目前的打算也是先學會用,然後一步步的弄明白實現原理。
然後開啟服務應該就成功了,注意,此處我省略了swagger的部分。
來看看現在Eureka中註冊的服務:
我只打開了user-service這個服務,其他的服務現在測試就不需要打開了
下面是我的測試過程
springcloud config主要是能夠幫助同一種服務能夠統一配置,配置mysql使用者名稱之類的東西。
實現了配置服務單點,肯定也要實現高可用。下面來說說如何實現高可用的配置服務中心
我們在 Eureka中通過服務名來訪問到配置服務,而不是指定 ip:port 的方式,就可以實現配置服務中心高可用化了。
修改 User服務的配置檔案
spring.application.name=user-service
server.port=8607
# 配置服務相關
#spring.cloud.config.uri=http://localhost:8400/
#spring.cloud.config.label=master
spring.cloud.config.profile=dev
spring.cloud.config.discovery.enabled=true
spring.cloud.config.discovery.service-id=configmanager
# 服務發現相關
eureka.client.serviceUrl.defaultZone=http://localhost:8500/eureka/,http://peer1:8501/eureka/,http://peer2:8502/eureka/
這裡我註釋掉了通過url訪問配置中心的方式,而是採取利用 Eureka 做服務發現,利用 configmanager 這個配置服務的服務名來做訪問的。其他都不用改。修改待查詢的配置檔案裡面的nickname的值
然後測試,結果如下
看起來現在咱們還是單點的,其實不然,只要使用 spring.profile.active 多建立幾個configmananger的例項就可以了,目前我不這樣做是因為,接下來將要介紹的是 負載均衡的內容,在沒有討論負載均衡的時候節點弄多了,效果不太好。
不過,目前我們的配置是隻能載入一次,如果在執行的過程中想要改變配置,需要做以下配置
在專案中引入 actuator ,對 spring boot進行監控
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
然後在配置檔案中加入:
# actuator
management.server.port=9001
#修改訪問路徑 2.0之前預設是/ 2.0預設是 /actuator 可以通過這個屬性值修改
management.endpoints.web.base-path=/monitor
#開放所有頁面節點 預設只開啟了health、info兩個節點
management.endpoints.web.exposure.include=*
#顯示健康具體資訊 預設不會顯示詳細資訊
management.endpoint.health.show-details=always
這個模組的作用主要就是監聽我們的spring boot專案,維護專案的執行情況,根據這樣的配置,我們就可以使用 http://localhost:9001/monitor/health 來檢視系統的健康狀況。使用 http://localhost:9001/monitor/refresh來動態修改配置檔案的更新。
測試一下
先測試一下 nickName當前的值
然後修改
使用 http://localhost:9001/monitor/refresh 的 post請求方法來動態載入更新
返回nickName說明nickName已經重新載入了,然後再在客戶端查詢nickName的值,發現已經改了
為了給大家做一個勵志的榜樣,我動態的把這句話補充完整
現在基本的架構已經有了,但是我們的Eureka的功能還沒有用起來,除了configmanager使用了服務發現功能,其他服務都沒有使用服務發現的功能,服務發現、負載均衡和服務治理是springcloud的關鍵之處。接下來先來說說服務發現。
在專案中加入ribbon,這是服務發現的時候,使用負載均衡的演算法
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
</dependency>
然後,在主程式中這樣寫:
@ServletComponentScan
@Configuration
@EnableAutoConfiguration
@EnableDiscoveryClient
@ComponentScan({"com.xinzu.user"})
public class UserApplication {
@Bean
@LoadBalanced
RestTemplate restTemplate(){
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(UserApplication.class, args);
}
}
@LoadBalanced 這個註解就是使用了負載均衡
然後,我們改造 pingjiaservice這個服務,改造的程式碼如下,實際上就是實現了一個rest介面,讓userservice來呼叫。
@RefreshScope
@Controller
public class TestController {
@Value("${nickName}")
private String nickName;
@Value("${passWord}")
private String passWord;
@RequestMapping(value = Path.TEST_FUWUFAXIAN, method = RequestMethod.GET)
@ResponseBody
public Integer getnickName(){
int ans1 = Integer.parseInt(nickName);
int ans2 = Integer.parseInt(passWord);
return ans1 + ans2;
}
}
在userservice中建立一個方法來呼叫上面的controller
@ApiOperation(value = "測試服務發現", notes = "測試服務發現")
@RequestMapping(value = Path.TESTCONSUMER, method = RequestMethod.GET)
@ResponseBody
public TestConfigDto getPingJia(){
TestConfigDto dto = new TestConfigDto();
dto.setCode(400);
dto.setInfo("沒有獲取到");
try {
String tmp = restTemplate.getForEntity("http://PINGJIA-SERVICE/testfuwufaxian", String.class).getBody();
tmp = tmp.replace("<Integer>","");
tmp = tmp.replace("</Integer>","");
Integer ans1 = Integer.parseInt(tmp);
String nickName1 = nickName + ans1;
dto.setCode(200);
dto.setInfo(nickName1);
}catch (Exception e){
System.out.println("出現了異常");
}
return dto;
}
啟動被呼叫的服務,開啟三個例項
然後在user這個服務的swagger ui介面上進行測試,發現結果如下。
至於那兩個加起來的數字是多少,這是一個懸念,哈哈。
由於篇幅有限,關於服務容錯保護,api網管服務這裡就不進行配置了。等我專案做完了再來補坑。