一、服務註冊引入
在上一篇環境與工程搭建,我們遠程調用時候寫url寫死了。
String url = "http://127.0.0.1:9090/product/"+ orderInfo.getProductId();
當更換服務器的時候,這個url是需要跟着變的。我們這裏就需要使用註冊中心來解決這個問題。
二、 服務註冊中心
註冊中心:維護⼀個服務列表,哪個機器上線了,哪個機器宕機了,這些信息都會⾃動更新到服務列表上,客⼾端拿到這個列表,直接進⾏服務調⽤即可。這個就是註冊中⼼
註冊中⼼主要有三種⻆⾊(以租房為例):
- 服務提供者(Server):⼀次業務中,被其它微服務調⽤的服務。也就是提供接⼝給其它微服務。(相當於房東)
- 服務消費者(Client):⼀次業務中,調⽤其它微服務的服務。也就是調⽤其它微服務提供的接⼝。(相當於租客)
- 服務註冊中⼼(Registry):⽤於保存Server 的註冊信息,當Server 節點發⽣變更時,Registry 會同步變更。服務與註冊中⼼使⽤⼀定機制通信,如果註冊中⼼與某服務⻓時間⽆法通信,就會註銷該實例。(相當於中介)
關係與工作內容:
- 服務註冊:服務提供者在啓動時,向 Registry 註冊⾃⾝服務,並向 Registry 定期發送⼼跳彙報存活狀態。
- 服務發現:服務消費者從註冊中⼼查詢服務提供者的地址,並通過該地址調⽤服務提供者的接⼝。服務發現的⼀個重要作⽤就是提供給服務消費者⼀個可⽤的服務列表。
三、 CAP理論
- 一致性(Consistency): CAP理論中的⼀致性, 指的是強⼀致性,所有節點在同⼀時間對外具有相同的數據。(弱一致性就是,所有節點對外達到相同數據可以有時間間隔)
- 可⽤性(Availability):保證每個請求都有響應(響應結果可能不對)
- 分區容錯性(Partition Tolerance):當出現⽹絡分區後,系統仍然能夠對外提供服務
分佈式系統CAP三個屬性是不可能同時滿足的,系統間的⽹絡又不能100%保證健康,服務⼜必須對外保證服務,因此Partition Tolerance(分區容錯性)不可避免。那就只能在C和A中選擇⼀個。也就是CP或者AP架構。
- CP架構:為了保證分佈式系統對外的數據⼀致性,於是選擇不返回任何數據
- AP架構:為了保證分佈式系統的可⽤性,節點間可能返回不同版本的數據(即使這個數據不正確)
四、 常見註冊中心
- Zookeeper Zookeeper的官⽅並沒有説它是⼀個註冊中⼼,但是國內Java體系,⼤部分的集羣環境都是依賴 Zookeeper來完成註冊中⼼的功能。
- Eureka Eureka是Netflix開發的基於REST的服務發現框架,主要⽤於服務註冊,管理,負載均衡和服務故障轉移。 官⽅聲明在Eureka2.0版本停⽌維護,不建議使⽤。但是Eureka是SpringCloud服務註冊/發現的默認實現,所以⽬前還是有很多公司在使⽤。
- Nacos Nacos是Spring Cloud Alibaba架構中重要的組件,除了服務註冊,服務發現功能之外,Nacos還⽀持配置管理,流量管理,DNS,動態DNS等多種特性。
五、 Eureka
Eureka主要分為兩個部分:
- Eureka Server:作為註冊中⼼Server端,向微服務應⽤程序提供服務註冊,發現,健康檢查等能⼒。
- Eureka Client:服務提供者,服務啓動時,會向Eureka Server 註冊⾃⼰的信息(IP,端⼝,服務信息等),Eureka Server 會存儲這些信息。
5.1 搭建註冊中心
我們就使用上一篇文章環境與工程搭建的案例代碼來搭建。
-
- 創建Eureka-server ⼦模塊
-
- pom中引⼊eureka-server依賴與項目構建插件
<!-- eureka-server依賴 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<!-- 項目構建插件 -->
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
- 完善啓動類
package com.cloud.eureka;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@EnableEurekaServer
@SpringBootApplication
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}
- 配置文件
server:
port: 10010
spring:
application:
name: eureka-server
eureka:
instance:
hostname: localhost
client:
fetch-registry: false # 表⽰是否從Eureka Server獲取註冊信息,默認為true.因為這是⼀個單點的Eureka Server,不需要同步其他的Eureka Server節點的數據,這⾥設置為false
register-with-eureka: false # 表⽰是否將⾃⼰註冊到Eureka Server,默認為true.由於當前應⽤就是Eureka Server,故⽽設置為false.
service-url: # 設置Eureka Server的地址,查詢服務和註冊服務都需要依賴這個地址.
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
- 啓動服務,訪問註冊中心
5.2 服務註冊
把product-service 註冊到eureka-server中。
- 在product-service的pom文件中加上eureka-client的依賴
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
- 完善product-service的配置⽂件
spring:
application:
name: product-service
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:10010/eureka
- 啓動服務
5.3 服務發現
修改order-service,在遠程調⽤時,從eureka-server拉取product-service的服務信息,實現服務發現
- 在order-service的pom文件中加上eureka-client的依賴
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
- 完善order-service的配置⽂件
spring:
application:
name: order-service
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:10010/eureka
- 遠程調⽤,修改service
package com.cloud.order.service;
import com.cloud.order.mapper.OrderMapper;
import com.cloud.order.model.OrderInfo;
import com.cloud.order.model.ProductInfo;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.netflix.eureka.EurekaServiceInstance;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import java.util.List;
@Service
@Slf4j
public class OrderService {
@Autowired
private OrderMapper orderMapper;
@Autowired
private RestTemplate restTemplate;
@Resource
private DiscoveryClient discoveryClient;
public OrderInfo selectOrderById(Integer orderId) {
OrderInfo orderInfo = orderMapper.selectOrderById(orderId);
//String url = "http://127.0.0.1:9090/product/"+ orderInfo.getProductId();
//根據應⽤名稱獲取服務列表
List<ServiceInstance> instances = discoveryClient.getInstances("product-service");
//服務可能有多個, 獲取第⼀個
EurekaServiceInstance instance = (EurekaServiceInstance) instances.get(0);
log.info(instance.getInstanceId());
//拼接url
String url = instance.getUri()+"/product/"+ orderInfo.getProductId();
ProductInfo productInfo = restTemplate.getForObject(url, ProductInfo.class);
orderInfo.setProductInfo(productInfo);
return orderInfo;
}
}
- 啓動