一、搭建服務註冊與發現中⼼
使⽤Spring Cloud Netflix 中的 Eureka 搭建服務註冊與發現中⼼
1、建立SpringBoot應用新增依賴
1、spring web2、eureka server
2、配置服務註冊與發現中⼼
## 設定服務註冊與發現中⼼的端⼝ server: port: 8761 ## 在微服務架構中,服務註冊中⼼是透過服務應⽤的名稱來區分每個服務的 ## 我們在建立每個服務之後,指定當前服務的 應⽤名/項⽬名 spring: application: null name: service-eureka eureka: client: ## ip 就是服務註冊中⼼伺服器的ip,port 就是服務註冊與發現中⼼設定的port service-url: defaultZone: 'http://192.168.54.59:8761/eureka' ## 設定服務註冊與發現中⼼是否為為叢集搭建(如果為叢集模式,多個eureka節點之間需要相互註冊) register-with-eureka: false ## 設定服務註冊與發現中是否作為服務進⾏註冊 fetch-registry: false
3、在啟動類新增@EnableEurekaServer註解
@SpringBootApplication @EnableEurekaServer public class ServiceEurekaApplication { public static void main(String[] args) { SpringApplication.run(ServiceEurekaApplication.class, args); } }
4、運⾏及訪問
二、服務註冊
建立儲存訂單的服務(order-add)註冊到服務註冊與發現中⼼
1、建立SpringBoot應⽤
建立spring boot應⽤,完成功能開發
2、註冊服務
將能夠完成特定業務的SpringBoot應⽤作為服務提供者,註冊到服務註冊與發現中⼼
2.1、新增依賴eureka-server
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency>
2.2、配置application.yml
server: port: 9001 ## 當前應⽤名會作為服務唯⼀標識註冊到eureka spring: application: name: order-add datasource: driver-class-name: com.mysql.jdbc.Driver url: 'jdbc:mysql://localhost:3306/db_2010_sc?characterEncoding=utf-8' username: root password: admin123 mybatis: mapper-locations: 'classpath:mappers/*' type-aliases-package: com.qfedu.order.beans ## 配置Eureka服務註冊與發現中⼼的地址 eureka: client: service-url: defaultZone: 'http://localhost:8761/eureka'
2.3、在當前服務應⽤的啟動類新增 @EnableEurekaClient 註解
@SpringBootApplication @MapperScan("com.qfedu.order.dao") @EnableEurekaClient public class OrderAddApplication { public static void main(String[] args) { SpringApplication.run(OrderAddApplication.class, args); } }
三、服務發現-Ribbon
服務消費者(api-order-add)透過eureka查詢服務提供者(order-add),透過服務調⽤元件調⽤提供者
- eureka server
- ribbon
1、基礎配置
Ribbon客戶端已經停更進維啦
1.1、建立SpringBoot應⽤,新增依賴
- eureka server
- ribbon
1.2、配置application.yml
server: port: 8001 spring: application: name: api-order-add eureka: client: service-url: defaultZone: 'http://localhost:8761/eureka'
1.3、在啟動類新增 @EnableDiscoveryClient註解
@SpringBootApplication @EnableDiscoveryClient public class ApiOrderAddApplication { public static void main(String[] args) { SpringApplication.run(ApiOrderAddApplication.class, args); } }
2、服務調⽤
2.1、配置RestTemplate
@Configuration public class AppConfig { @LoadBalanced //啟⽤Ribbon(負載均衡) @Bean public RestTemplate getRestTemplate(){ return new RestTemplate(); } }
2.2、在Service中注⼊RestTemplate物件調⽤服務
@Service public class OrderAddServiceImpl implements OrderAddService { @Autowired private RestTemplate restTemplate; @Override public ResultVO saveOrder(Order order) { //1. 調⽤ order-add服務進⾏儲存 ResultVO vo = restTemplate.postForObject("http://order-add/order/add", order, ResultVO.class); //2. 調⽤ orderitem-add 儲存訂單快照 //3. 調⽤ stock-update 修改商品庫存 //4. 調⽤ shopcart-del 刪除購物⻋記錄 return null; } }
3、Ribbon服務調⽤說明
@LoadBalanced註解是Ribbon的⼊⼝,在RestTemplate物件上新增此註解之後,再使⽤RestTemplate傳送REST請求的時候,就可以透過Ribbon根據服務名稱從Eureka中查找服務對應的訪問地址列表,再根據負載均衡策略(預設輪詢)選擇其中的⼀個,然後完成服務的調⽤
- 獲取服務列表
- 根據負載均衡策略選擇服務
- 完成服務調⽤
四、基於Ribbon進⾏服務調⽤的引數傳遞
1、RestTemplate傳送調⽤請求的⽅法
SpringCloud的服務調⽤是基於REST的,因此當服務提供者規定了請求的⽅式,服務消費者必須傳送對應⽅式的請求才能完成服務的調⽤,RestTemplate提供了多個⽅法⽤於傳送不同形式的請求。
//post⽅式請求 restTemplate.postForObject(); //get⽅式請求 restTemplate.getForObject(); //delete⽅式請求 restTemplate.delete(); //put⽅式請求 restTemplate.put();
2、put/post請求傳參
1、服務消費者請求傳參
//引數1:訪問服務的url //引數2:傳遞的物件引數 //引數3:指定服務提供者返回的資料型別 ResultVO vo = restTemplate.postForObject("http://order-add/order/add",order, ResultVO.class);
2、服務提供者接收引數
@PostMapping("/add") public ResultVO addOrder(@RequestBody Order order){ return orderService.saveOrder(order); }
3、get請求傳參
1、服務消費者請求傳參
String userId = order.getUserId();
ResultVO vo = restTemplate.getForObject("http://order-add/order/add?userId="+userId, ResultVO.class);
2、服務提供者接收引數
@GetMapping("/add") public ResultVO addOrder(Order order){ return orderService.saveOrder(order); } @GetMapping("/add") public ResultVO addOrder(String userId){ //return orderService.saveOrder(order); }
五、服務發現-Feign
1、基礎配置
1.1、建立SpringBoot應⽤,新增依賴
spring webeureka serverOpenFeign
1.2、配置application.yml
server: port: 8002 spring: application: name: api-order-add-feign eureka: client: service-url: defaultZone: 'http://localhost:8761/eureka'
1.3、在啟動類新增註解
@SpringBootApplication @EnableDiscoveryClient //宣告為服務消費者 @EnableFeignClients //宣告啟⽤feign客戶端 public class ApiOrderAddFeignApplication { public static void main(String[] args) { SpringApplication.run(ApiOrderAddFeignApplication.class, args); } }
2、服務調⽤
使⽤Feign進⾏服務調⽤的時候,需要⼿動建立⼀個服務訪問客戶端(接⼝)
2.1、建立Feign客戶端
@FeignClient("order-add") public interface OrderAddClient { @PostMapping("order/add") public ResultVO addOrder(Order order); }
2.2、使⽤Feign客戶端調⽤服務
@Service public class OrderAddServiceImpl implements OrderAddService { @Autowired private OrderAddClient orderAddClient; @Override public ResultVO saveOrder(Order order) { //1. 調⽤ order-add服務進⾏儲存 ResultVO vo = orderAddClient.addOrder(order); //2. 調⽤ orderitem-add 儲存訂單快照 //3. 調⽤ stock-update 修改商品庫存 //4. 調⽤ shopcart-del 刪除購物⻋記錄 return vo; } }
3、Feign傳參
3.1、POST請求
1、透過請求體傳遞物件
服務提供者@PostMapping("/add") public ResultVO addOrder(@RequestBody Order order){ System.out.println("-------------------order-add"); System.out.println(order); return orderService.saveOrder(order); }服務消費者(Feign客戶端)@FeignClient("order-add") public interface OrderAddClient { @PostMapping("order/add") public ResultVO addOrder(Order order); }
2、透過請求⾏傳參
服務提供者@PostMapping("/add") public ResultVO addOrder(@RequestBody Order order,String str){ System.out.println("-------------------order-add"); System.out.println(order); System.out.println(str); return orderService.saveOrder(order); }服務消費者(Feign客戶端)//1.對⽤POST請求調⽤服務,Feign客戶端的⽅法引數預設為body傳值(body只能有⼀個值) //2.如果有多個引數,則需要透過@RequestParam宣告引數為請求⾏傳值 @PostMapping("order/add") public ResultVO addOrder(Order order,@RequestParam("str") String str);
3、Get請求
Get請求調⽤服務,只能透過url傳參在Feign客戶端的⽅法中,如果不指定引數的傳值⽅式,則預設為body傳參,Get請求也不例外;因此對於get請求傳遞引數,必須透過@RequestParam註解宣告
服務提供者@GetMapping("/get") public Order addOrder(String orderId){ return new Order(); }服務消費者(Feign客戶端)@GetMapping("order/get") public Order getOrder(@RequestParam("orderId") String orderId);
六、服務註冊與發現中⼼的可靠性和安全性
1、可靠性
在微服務架構系統中,服務消費者是透過服務註冊與發現中⼼發現服務、調⽤服務的,服務註冊與發現中⼼伺服器⼀旦掛掉,將會導致整個微服務架構系統的崩潰,如何保證Eureka的可靠性呢?
- 使⽤eureka叢集
Eureka叢集搭建
相互註冊、相互發現
## 設定服務註冊與發現中⼼的端⼝ server: port: 8761 ## 在微服務架構中,服務註冊中⼼是透過服務應⽤的名稱來區分每個服務的 ## 我們在建立每個服務之後,指定當前服務的 應⽤名/項⽬名 spring: application: name: service-eureka eureka: client: ## 設定服務註冊與發現中⼼是否為叢集搭建 register-with-eureka: true ## 設定服務註冊與發現中是否作為服務進⾏註冊 fetch-registry: true ## ip 就是服務註冊中⼼伺服器的ip ## port 就是服務註冊與發現中⼼設定的port service-url: defaultZone: 'http://192.168.54.10:8761/eureka'
2、安全性
當完成Eureka的搭建之後,只要知道ip和port就可以隨意的註冊服務、調⽤服務,這是不安全的,我們可以透過設定帳號和密碼來限制服務的註冊及發現。
- 在eureka中整合Spring Security安全框架實現帳號和密碼驗證
2.1、新增SpringSecurity的依賴
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency>
2.2、設定訪問eureka的帳號和密碼
spring:
security:
user:
name: zhangsan
password: 123456
2.3、配置Spring Security
@Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable(); // 設定當前伺服器的所有請求都要使⽤spring security的認證 http.authorizeRequests().anyRequest().authenticated().and().httpBasic(); } }
2.4、服務提供者和服務消費者連線到註冊中⼼都要帳號和密碼
eureka: client: service-url: defaultZone: 'http://zhangsan:123456@localhost:8761/eureka'
七、熔斷器-Hystrix
服務故障的雪崩效應:當A服務調⽤B服務時,由於B服務的故障導致A服務處於阻塞狀態,當量的請求可能會導致A服務因資源耗盡⽽出現故障。
為了解決服務故障的雪崩效應,出現了熔斷器模型。
1、熔斷器介紹
熔斷器作⽤:
1、服務降級 :⽤戶請求A服務,A服務調⽤B服務,當B服務出現故障或者在特定的時間段內不能給A服務響應,為了避免A服務因等待B服務⽽產⽣阻塞,A服務就不等B服務的結果了,直接給⽤戶⼀個降級響應2、服務熔斷 :⽤戶請求A服務,A服務調⽤B服務,當B服務出現故障的頻率過⾼達到特定閾值(5s 20次)時,當⽤戶再請求A服務時,A服務將不再調⽤B服務,直接給⽤戶⼀個降級響應
2、熔斷器的原理
3、基於Ribbon服務調⽤的熔斷器使⽤
3.1、服務消費者的服務降級
1、新增熔斷器依賴 hystrix
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency>2、在啟動類新增 @EnableHystrix 註解
@SpringBootApplication @EnableDiscoveryClient @EnableHystrix public class ApiOrderAddApplication { public static void main(String[] args) { SpringApplication.run(ApiOrderAddApplication.class, args); } }3、在調⽤服務提供者的業務處理⽅法中,進⾏降級配置
@Service public class OrderAddServiceImpl implements OrderAddService { @Autowired private RestTemplate restTemplate; @HystrixCommand(fallbackMethod ="fallbackSaveOrder",commandProperties = { @HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value="3000") } ) public ResultVO saveOrder(Order order) { //1. 調⽤ order-add服務進⾏儲存 //引數1:訪問服務的url //引數2:傳遞的物件引數 //引數3:指定服務提供者返回的資料型別 ResultVO vo = restTemplate.postForObject("http://order-add/order/add",order, ResultVO.class); System.out.println(vo); return vo; } /** * 降級⽅法:與業務⽅法擁有相同的引數和返回值 * @return */ public ResultVO fallbackSaveOrder(Order order){ return ResultVO.fail("⽹絡異常,請重試!",null); } }
3.2、服務提供者的服務降級
1、配置步驟⼀致2、服務提供者接⼝降級
@RestController @RequestMapping("/order") public class OrderController { @Autowired private OrderService orderService; @HystrixCommand(fallbackMethod ="fallbackAddOrder",commandProperties = { @HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value="3000") } ) @PostMapping("/add") public ResultVO addOrder(@RequestBody Order order){ System.out.println("-------------------order-add"); System.out.println(order); try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } return orderService.saveOrder(order); } public ResultVO fallbackAddOrder(@RequestBody Order order){ System.out.println("-------------------order-add--fallback"); return ResultVO.fail("訂單儲存失敗!",null); } }
1、服務熔斷配置
熔斷器狀態:閉合、開啟、半開服務熔斷配置@HystrixCommand(fallbackMethod ="fallbackSaveOrder",commandProperties = { @HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value="3000"), @HystrixProperty(name="circuitBreaker.enabled",value="true"),//啟⽤服務熔斷 @HystrixProperty(name="circuitBreaker.sleepWindowInMilliseconds",value="10000"),//時間 @HystrixProperty(name="circuitBreaker.requestVolumeThreshold",value="10"),//請求次數 @HystrixProperty(name="circuitBreaker.errorThresholdPercentage",value="50"),//服務錯誤率 } ) public ResultVO saveOrder(Order order) { //1. 調⽤ order-add服務進⾏儲存 ResultVO vo = restTemplate.postForObject("http://orderadd/order/add", order, ResultVO.class); System.out.println(vo); return vo; } /** * 降級⽅法:與業務⽅法擁有相同的引數和返回值 * @return */ public ResultVO fallbackSaveOrder(Order order){ return ResultVO.fail("⽹絡異常,請重試!",null); }服務熔斷:當⽤戶請求服務A,服務A調⽤服務B時,如果服務B的故障率達到特定的閾值時,熔斷器就會被開啟⼀個時間週期(預設5s,可⾃定義),在這個時間週期內如果⽤戶請求服務A,服務A將不再調⽤服務B,⽽是直接響應降級服務。
4、基於Feign服務調⽤的熔斷器使⽤
Feign是基於Ribbon和Hystrix的封裝
4.1、Feign中的熔斷器使⽤
1、新增依賴(SpringBoot 2.3.11 、Spring Cloud H)
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.3.11.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <properties> <java.version>1.8</java.version> <spring-cloud.version>Hoxton.SR11</spring-cloud.version> </properties><dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency>2、在application.yml啟⽤熔斷器機制
feign: hystrix: enabled: true3、在啟動類新增 @EnableHystrix
@SpringBootApplication @EnableDiscoveryClient @EnableFeignClients @EnableHystrix public class ApiOrderAddFeignApplication { public static void main(String[] args) { SpringApplication.run(ApiOrderAddFeignApplication.class, args); } }4、建立服務降級處理類
5、FeignClient的服務降級類:
5.1.必須實現Feign客戶端接⼝
5.2.必須交給Spring容器管理
@Component public class OrderAddClientFallback implements OrderAddClient { public ResultVO addOrder(Order order, String str) { System.out.println("-------addOrder的降級服務"); return ResultVO.fail("fail",null); } public Order getOrder(String orderId) { System.out.println("-------getOrder的降級服務"); return new Order(); } }6、在Feign客戶端指定降級處理類
@FeignClient(value = "order-add", fallback =OrderAddClientFallback.class) public interface OrderAddClient { //1.對⽤POST請求調⽤服務,Feign客戶端的⽅法引數預設為body傳值(body只能有⼀個值) //2.如果有多個引數,則需要透過@RequestParam宣告引數為請求⾏傳值 @PostMapping("order/add") public ResultVO addOrder(Order order,@RequestParam("str") String str);
@GetMapping("order/get") public Order getOrder(@RequestParam("orderId") String orderId); }7、Service類透過Feign客戶端調⽤服務
@Service public class OrderAddServiceImpl implements OrderAddService { @Autowired private OrderAddClient orderAddClient; //當我們建立Feign客戶端的降級類並交給Spring管理後 在Spring容器中就會出現兩個OrderAddClient物件 @Override public ResultVO saveOrder(Order order) { //1. 調⽤ order-add服務進⾏儲存 ResultVO vo = orderAddClient.addOrder(order,"測試字串"); Order order1 = orderAddClient.getOrder("訂單編號"); System.out.println(order1); return vo; } }
5、Ribbon 引數配置
ribbon: ## Ribbon建⽴連線最⼤等待時間 ConnectTimeout: 1000 ## 在當前服務提供者嘗試連線次數 MaxAutoRetries: 2 ## 與服務提供者通訊時間 ReadTimeout: 5000 ## 設定熔斷器服務降級時間 (預設 1000) hystrix: command: default: execution: isolation: thread: timeoutInMilliseconds: 8000
6、熔斷器儀表盤監控
檢視各個服務的熔斷器狀態皮膚:
- 熔斷器儀表盤
6.1、搭建熔斷器儀表盤
1、建立SpringBoot項⽬,新增依賴
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrixdashboard</artifactId> </dependency>2、配置儀表盤的port和appName
server: port: 9999 spring: application: name: hystrix-dashboard hystrix: dashboard: proxy-stream-allow-list: localhost3、啟動類新增 @EanbleHystrixDashboard 註解
@SpringBootApplication @EnableHystrixDashboard public class HystrixDashboardApplication { public static void main(String[] args) { SpringApplication.run(HystrixDashboardApplication.class,args); } }4、訪問 http://localhost:9999/hystrix
6.2、配置使⽤了熔斷器的服務可被監控
1、新增依賴
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>2、配置(給每個需要監控熔斷器的專案配置)
@Configuration public class DashBoardConfig { @Bean public ServletRegistrationBean getServletRegistrationBean(){ HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet(); ServletRegistrationBean registrationBean = new ServletRegistrationBean(streamServlet); registrationBean.setName("HystrixMetricsStreamServlet"); registrationBean.setLoadOnStartup(1); registrationBean.addUrlMappings("/hystrix.stream"); return registrationBean; } }3、檢視指定服務的熔斷器⼯作引數