一、搭建服務註冊與發現中⼼
使⽤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:訪問服務的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集羣
相互註冊、相互發現
## 設置服務註冊與發現中⼼的端⼝ 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、查看指定服務的熔斷器⼯作參數