目錄
-
-
- SpringCloud簡介
- 搭建服務注冊中心 eureka-server
- 服務提供者
- 服務消費者
- RestTemplate常用方法
- 解決@ResponseBody、@RestController傳回xml而非json的問題
- Eureka的架構
- Eureka Server的服務保護機制
- Eureka Client的緩存機制
-
技術棧
- Eureka :服務治理
- Ribbon :負載均衡
- Feign :聲明式服務調用
- Hystrix:斷路器,容錯保護
- Zuul:網關(API Gateway),路由轉發、請求過濾
- Config:配置中心,集中管理配置
- Bus:總線
- Sleuth+Zipkin:分布式鍊路追蹤
Eureka、Ribbon、Feign、Hystrix都是netflix的開源項目,SpringCloud都将它們內建進來了。
springcloud的版本
其它架構的版本是1.x,2.x…,SpringCloud的版本是Axx、Bxx、Cxx這樣命名走的。
ga是穩定版、通用版,snapshot是開發版。
springcloud需要自己寫一個服務(子產品),作為服務注冊中心。
1、建立子子產品eureka-server,建立時勾選 Spring Cloud Discovery -> Eureka Server ,或者手動添加依賴
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
2、引導類上加 @EnableEurekaServer
3、application.yml
server:
port: 8761
spring:
application:
name: eureka-server
eureka:
instance:
#所在機器的hostname或者ip
hostname: 127.0.0.1
client:
#不注冊到其它eureka server上
registerWithEureka: false
#不從其它eureka server上拉取、同步注冊資訊
fetchRegistry: false
#此eureka server綁定的注冊中心位址
serviceUrl:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
如果要搭建Eureka Server叢集,将上面2個false改為true即可。
啟動應用,通路 127.0.0.1:8761 即可看到eureka server的頁面。
1、建立子子產品order-service,建立時勾選Spring Cloud Discovery -> Eureka Discovery Client,也可以手動添加依賴
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
每個服務提供者、消費者都是Eureka Client
2、引導類上加 @EnableEurekaClient 或 @EnableDiscoveryClient
一個是eureka的注解、一個是springcloud的注解,效果一樣。也可以都不加,因為classpath中有eureka client的依賴 spring-cloud-starter-netflix-eureka-client 時,啟動應用時會自動在引導類上加 @EnableEurekaClient。
server:
port: 8771
spring:
application:
name: order-service #服務名稱
eureka:
client:
serviceUrl:
defaultZone: http://127.0.0.1:8761/eureka/ #注冊中心位址,如果是Eureka Server叢集,url之間逗号分隔
不用寫全部的Eureka Server的url,寫一部分即可。注冊時隻注冊到第一個url上,Eureka Server之間會同步資料,後面的url都是備胎,前面的故障時才會使用後一個。
4、model、service、controller
mdoel
@Getter
@Setter
public class Order implements Serializable {
}
service
public interface OrderService {
List<Order> findOrdersByUserId(Integer userId);
}
@Service
public class OrderServiceImpl implements OrderService {
@Override
public List<Order> findOrdersByUserId(Integer userId) {
}
}
controller
@Controller
@RequestMapping("/api/v1/order")
public class OrderController {
@Autowired
private OrderService orderService;
@RequestMapping("/list/{user_id}")
@ResponseBody
public List<Order> findOrdersByUserId(@PathVariable("user_id") Long userId){
}
}
1、建立子子產品user-service,建立時勾選Spring Cloud Discovery -> Eureka Discovery Client
2、引導類
@SpringBootApplication
@EnableEurekaClient //非必需
public class UserServiceApplication {
@Bean
@LoadBalanced
public RestTemplate restTemplate() { //建立RestTemplate這個bean
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(UserServiceApplication.class, args);
}
}
server:
port: 8781
spring:
application:
name: user-service #服務名稱
eureka:
client:
serviceUrl:
defaultZone: http://127.0.0.1:8761/eureka/ #注冊中心位址,如果是Eureka Server叢集,url之間逗号分隔
model
@Getter
@Setter
public class Order implements Serializable {
}
代碼備援多,如果要使用其它服務中的類,需要copy過去才能使用。可以用一個單獨的子產品寫公用的類,打包為jar,再要使用的服務中引用。
public interface UserService {
List<Order> findOrdersById(Integer id);
}
@Service
public class UserServiceImpl implements UserService {
@Autowired //注入RestTemplate
private RestTemplate restTemplate;
@Override
public List<Order> findOrdersById(Integer id){
// 遠端調用
return restTemplate.getForObject("http://order-service/api/v1/order/list/" + id, List.class);
}
}
controller
@Controller
@RequestMapping("/api/v1/user")
public class UserController {
@Autowired
private UserService UserService;
@RequestMapping("/{user_id}/order")
@ResponseBody
public List<Order> findOrdersById(@PathVariable("user_id") Integer id){
return userService.findOrdersById(id);
}
}
RestTemplate用于以restful方式遠端調用服務,常用方法如下
//參數:遠端服務接口,傳回值類型(目标類型)
List orders = restTemplate.getForObject("http://order-service/api/v1/order/list/" + userId, List.class);
//url中的參數可以使用+号拼接,也可以使用占位符{1},{2}...,在目标類型後面寫參數值(Object...)
List orders = restTemplate.getForObject("http://order-service/api/v1/order/list/{1}" + userId, List.class,1);
//可以用map向服務提供者傳遞資料
HashMap<String, Object> map = new HashMap<>();
List orders = restTemplate.getForObject("http://order-service/api/v1/order/list/" + userId, List.class,map);
xxxForObject(),get是查詢,post是建立,put是更新,delete是删除,用法都差不多。post、put可混用。
在controller上用produces指定為json格式,預設是xml
@RequestMapping(value = "/api/v1/user",produces = "application/json;charset=utf-8")
Eureka Server預設開啟了服務保護:
Eureka Server在90s内都未收到某個服務節點的心跳包時,會開啟為期15min的心跳檢測,這期間不會把該服務節點的資訊從服務注冊清單中删除。Eureka Server控制台也會用一段紅色文字提示:這個服務節點進入了服務保護模式。
如果15min内實際收到該節點的心跳數占應接收心跳數的比值小于0.85,就認為是該服務節點與Eureka Server之間的網絡故障導緻的心跳包丢失,該服務節點本身沒有問題,保留該節點的注冊資訊;如果比值大于0.85,就認為是該服務節點自身出了問題,從服務注冊清單中删除該服務節點的資訊。
造成的問題:某個服務節點可能真的出問題了,但依然被當做正常節點使用。
項目上線時一般也不關閉服務保護,公司的項目,服務節點數量往往成百上千,如果關閉服務保護,網絡不穩定時(網絡波動、延遲、斷線等)會導緻大量的服務反複注冊、删除、再注冊,會降低程式性能。開啟服務保護,某些服務節點故障時,無非就是注冊清單中有少量無效節點,不一定會調用叢集中的這些無效節點,就算調用了無效節點不能正确處理請求,使用者重新整理幾次,轉發給叢集中正常的節點處理就ok了。
開發、調試時可以關閉Eureka Server的服務保護,快速清理掉無效節點
eureka:
server:
#關閉Eureka Server的服務保護
enableSelfPreservation: false
Eureka Client | ZK Client都會緩存Eureka Server | ZK Server傳回的服務節點清單,緩存一直有效,直到此Client下線。