一.背景
1.問題提出
我們在一個父項目下寫了兩個子項目,需要兩個子項目之間相互調用。我們可以發送HTTP請求來獲取我們想要的資源,具體實現的方法有很多,可以用HttpURLConnection、HttpClient、Okhttp、 RestTemplate等。
舉個例子,我們想要通過訂單表中的商品Id來查詢商品的詳細信息,我們需要向商品所在的模塊發送HTTP請求,通過請求獲取商品的詳細信息:
@Resource
RestTemplate restTemplate;@RequestMapping("/{orderId}")
public OrderInfo getOrderById(@PathVariable("orderId") Integer orderId){OrderInfo orderInfo = orderMapper.selectByOrderId(orderId);String url="http://127.0.0.1:9090/product/"+orderInfo.getProductId();ProductInfo productInfo = restTemplate.getForObject(url, ProductInfo.class);orderInfo.setProductInfo(productInfo);return orderInfo;
}
上面的實現有一個問題:如果機器發生變故,url會跟著變更。url變來變去,我們寫的代碼就要進行修改,因此我們要想辦法解決這個問題。
2.解決思路
注冊中心可以解決這個問題。
服務啟動或變更的時候,向注冊中心報道,注冊中心就存儲著應用和IP的關系。
當我們想要調用方法的時候,只需要向注冊中心去獲取服務方的IP就行了。
那什么是注冊中心?
注冊中心(Service Registry)?是分布式系統(尤其是微服務架構)中的核心組件,主要用于解決服務實例的?自動發現、動態管理和路由調度?問題。它就像一個 “電話簿”,記錄著所有服務實例的網絡地址(IP、端口等信息),并實時更新這些信息,確保服務之間可以快速、可靠地通信。
注冊中心有三種角色:服務提供者(Server)、服務消費者(Client)、服務注冊中心(Registry)。
它們之間的關系可以通過兩個概念來描述:
服務注冊:服務提供者在啟動的時候向服務注冊中心(Registry)注冊自身服務,并向服務注冊中心(Registry)定期發送心跳匯報存好情況。
服務發現:服務消費者從注冊中心查詢服務提供者的地址,并通過該地址調用服務提供者的接口。
3.CAP理論
Consistency,一致性:這里的一致性指的是強一致性,所有結點在同一時間具有相同的數據;
Availability,可用性:保證每個請求都有響應;
Partition Tolerance,分區容錯性:分布式系統中的節點之間出現網絡分區(即部分節點無法通信)時,系統仍能繼續運行。
這個CAP是一個“不可能三角”,即這三個要求不能同時實現,要想實現CA就不能實現P,要實現AP就不能實現C。
在微服務系統中,P這一點是必須保證的,所有A和C只能實現一個。
二.Eureka
Eureka(尤里卡)曾是SpringCloud官方默認的注冊中心,其符合CAP理論中的AP,即保證分布式系統的可用性。
1.搭建服務中心
1)創建項目;
2)pom加入Eureka的依賴:
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
3)配置文件,加入相關配置:
server:port: 10010
spring:application:name: eureka-server
eureka:instance:hostname: localhostclient:fetch-registry: false # 表示是否從Eureka Server獲取注冊信息,默認為true.因為這是一個單點的Eureka Server,不需要同步其他的Eureka Server節點的數據,這里設置為falseregister-with-eureka: false # 表示是否將自己注冊到Eureka Server,默認為true.由于當前應用就是Eureka Server,故而設置為false.service-url:# 設置Eureka Server的地址,查詢服務和注冊服務都需要依賴這個地址defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
logging:pattern:console: '%d{MM-dd HH:mm:ss.SSS} %c %M %L [%thread] %m%n'
4)啟動類,開啟Eureka的功能:
@EnableEurekaServer
@SpringBootApplication
public class EurekaServerApplication {public static void main(String[] args) {SpringApplication.run(EurekaServerApplication.class,args);}
}
此時我們啟動項目,在瀏覽器上輸入:127.0.0.1:10010,如果出現以下頁面說明成功了:
2.服務注冊
1)加入Eureka依賴:
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
2)修改配置信息:
配置服務的名稱和eureka地址
spring:application:name: product-service
eureka:client:service-url:defaultZone: http://127.0.0.1:10010/eureka
3)啟動程序
啟動程序后出現下面的界面說明注冊成功了:
注意紅線圈出的部分,這個Application的名字與剛剛配置的名字是一樣的:product-service。
3.服務發現
1)加入Eureka依賴:
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
2)修改配置信息:
spring:application:name: order-service
eureka:client:service-url:defaultZone: http://127.0.0.1:10010/eureka
3)修改遠程調用的代碼
修改本篇文章一開始提出的問題代碼:
@Resource
RestTemplate restTemplate;@Resource
DiscoveryClient discoveryClient;@RequestMapping("/{orderId}")
public OrderInfo getOrderById(@PathVariable("orderId") Integer orderId){OrderInfo orderInfo = orderMapper.selectByOrderId(orderId);//從eureka中獲取服務列表List<ServiceInstance> instances = discoveryClient.getInstances("product-service");//寫法一:EurekaServiceInstance instance=(EurekaServiceInstance)instances.get(0);String url=instance.getUri()+"/product/"+orderInfo.getProductId();//寫法二://String uri = instances.get(0).getUri().toString();//String url=uri+"/product/"+orderInfo.getProductId();ProductInfo productInfo = restTemplate.getForObject(url, ProductInfo.class);orderInfo.setProductInfo(productInfo);return orderInfo;
}
兩種寫法都可以。?
4)啟動程序
如果出現下面界面就說明成功了: