微服務(wù)有哪些優(yōu)勢?
1.易于開發(fā)和維護(hù)
2.可以全自動部署
3.局部修改容易部署
4.技術(shù)棧不受限
微服務(wù)有哪些挑戰(zhàn)?
1.運(yùn)維成本比較高
2.分布式固有的復(fù)雜性
3.接口調(diào)整成本高
微服務(wù)設(shè)計(jì)原則?
1.單一職責(zé)原則
2.服務(wù)自治原則
3.輕量級通信原則
4.接口明確原則
常用的微服務(wù)有哪些,spring cloud作為其中一個(gè)微服務(wù)有哪些優(yōu)勢?
常見的微服務(wù)有spring cloud、dubbo等。它們的區(qū)別很多,比如說調(diào)用方式,spring cloud采用REST API方式,dubbo采用RPC;spring cloud幾乎涉及到分布式組件各個(gè)方面,而dubbo僅僅實(shí)現(xiàn)了其中的服務(wù)治理模塊。spring cloud優(yōu)勢在于它豐富的組件以及分布式解決方案集成springboot十分的方便快捷。
下面來談?wù)剆pringcloud豐富的組件和模塊,下面只給出使用說明,概念性的說明省略。
1.服務(wù)發(fā)現(xiàn)組件
Eureka
application.yml如下配置
security:
basic:
enabled: true
user:
name: user
password: password123
server:
port: 8761
eureka:
client:
register-with-eureka: false
fetch-registry: false
service-url:
defaultZone: http://user:password123@localhost:8761/eureka
EurekaApplication.java啟動類
@SpringBootApplication
@EnableEurekaServer
public class EurekaApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaApplication.class, args);
}
}
2.服務(wù)注冊(消費(fèi)者和服務(wù)者都會注冊服務(wù))
application.yml如下配置
eureka:
client:
healthcheck: #健康檢查 配合actuator
enabled: true
serviceUrl:
defaultZone: http://user:password123@localhost:8761/eureka #權(quán)限驗(yàn)證
instance:
prefer-ip-address: true #服務(wù)可以ip調(diào)用
instance-id: ${spring.application.name}:${spring.cloud.client.ipAddress}:${spring.application.instance_id:${server.port}}
PS:eureka會將所有注冊的服務(wù)按照application.name分組,availability zones標(biāo)識集群節(jié)點(diǎn)數(shù),status會顯示各個(gè)實(shí)例狀態(tài)(up表示可用),實(shí)例id可以用eureka.instance.instance-id來指定
3.服務(wù)消費(fèi)ribbon
application.yml如何配置
eureka:
client:
healthcheck:
enabled: true
serviceUrl:
defaultZone: http://user:password123@localhost:8761/eureka
ConsumerMovieRibbonApplicatio.java 啟動類
@SpringBootApplication
@EnableEurekaClient
@RibbonClient(name = "microservice-provider-user", configuration = TestConfiguration.class) //允許自定義ribbonclient
@ComponentScan(excludeFilters = { @ComponentScan.Filter(type = FilterType.ANNOTATION, value = ExcludeFromComponentScan.class) }) //保留默認(rèn)的ribboclient
public class ConsumerMovieRibbonApplication {
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(ConsumerMovieRibbonApplication.class, args);
}
}
@Configuration
@ExcludeFromComponentScan
public class TestConfiguration {
// @Autowired
// IClientConfig config;
@Bean
public IRule ribbonRule() {
return new RandomRule();
}
}
//注意這邊使用virtual hostname(provider applicationName),ribbon會根據(jù)virtual hostname找到對應(yīng)幾個(gè)服務(wù),然后根據(jù)負(fù)載均衡算法選擇一個(gè)服務(wù)
this.restTemplate.getForObject("http://microservice-provider-user/simple/" + id, User.class);
PS:自定義負(fù)載均衡算法:默認(rèn)是round,還有random、高可用、響應(yīng)時(shí)間等
4.服務(wù)消費(fèi)feign 聲明式httpclient
ConsumerMovieFeignApplication.啟動類
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
public class ConsumerMovieFeignApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerMovieFeignApplication.class, args);
}
}
新建服務(wù)調(diào)用方一致的方法,方法上注解基本同springmvc一致
@FeignClient("microservice-provider-user") //對應(yīng)provider serviceId
public interface UserFeignClient {
@RequestMapping(value = "/simple/{id}", method = RequestMethod.GET)
public User findById(@PathVariable("id") Long id); // 坑:1. @GetMapping不支持 2. @PathVariable得設(shè)置value 3.參數(shù)如果是復(fù)雜對象時(shí),即使指定GET方法也失效
@RequestMapping(value = "/user", method = RequestMethod.POST)
public User postUser(@RequestBody User user);
// 該請求不會成功,只要參數(shù)是復(fù)雜對象,即使指定了是GET方法,feign依然會以POST方法進(jìn)行發(fā)送請求??赡苁俏覜]找到相應(yīng)的注解或使用方法錯(cuò)誤
@RequestMapping(value = "/get-user", method = RequestMethod.GET)
public User getUser(User user);
}
5.eureka實(shí)現(xiàn)高可用
eureka配置
spring:
application:
name: EUREKA-HA
---
server:
port: 8761
spring:
profiles: peer1
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8762/eureka/,http://localhost:8763/eureka/
---
server:
port: 8762
spring:
profiles: peer2
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/,http://localhost:8763/eureka/
---
server:
port: 8763
spring:
profiles: peer3
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/,http://localhost:8762/eureka/
PS:實(shí)現(xiàn)原理就是交叉注冊,類似于主節(jié)點(diǎn)的備份,每個(gè)節(jié)點(diǎn)都在剩下的節(jié)點(diǎn)都有備份,從而實(shí)現(xiàn)集群
6.斷路器hystrix
流程說明:
- ①.每次調(diào)用創(chuàng)建一個(gè)新的HystrixCommand,把依賴調(diào)用封裝在run()方法中.
- ②.執(zhí)行execute()/queue做同步或異步調(diào)用.
- ③.是否開啟請求緩存,如果開啟了,并且如果緩存中對請求的響應(yīng)可用,則此緩存響應(yīng)將立即以“Observable”的形式返回。
- ④.判斷熔斷器(circuit-breaker)是否打開,如果打開跳到步驟8,進(jìn)行降級策略,如果關(guān)閉進(jìn)入步驟.
- ⑤.判斷線程池/隊(duì)列/信號量是否跑滿,如果跑滿進(jìn)入降級步驟8,否則繼續(xù)后續(xù)步驟.
- ⑥.調(diào)用HystrixCommand的run方法.運(yùn)行依賴邏輯
- (1).依賴邏輯調(diào)用超時(shí),進(jìn)入步驟8.
- ⑦.判斷邏輯是否調(diào)用成功
- (1).返回成功調(diào)用結(jié)果
- (2).調(diào)用出錯(cuò),進(jìn)入步驟8.
- ⑧.計(jì)算熔斷器狀態(tài),所有的運(yùn)行狀態(tài)(成功, 失敗, 拒絕,超時(shí))上報(bào)給熔斷器,用于統(tǒng)計(jì)從而判斷熔斷器狀態(tài).
- ⑨.getFallback()降級邏輯.
- 1.以下四種情況將觸發(fā)getFallback調(diào)用:
- (1):run()方法拋出非HystrixBadRequestException異常。
- (2):run()方法調(diào)用超時(shí)
- (3):熔斷器開啟攔截調(diào)用
- (4):線程池/隊(duì)列/信號量是否跑滿
- 2.沒有實(shí)現(xiàn)getFallback的Command將直接拋出異常
- 3.fallback降級邏輯調(diào)用成功直接返回
- 4.降級邏輯調(diào)用失敗拋出異常
- 1.以下四種情況將觸發(fā)getFallback調(diào)用:
- ⑩.返回執(zhí)行成功結(jié)果
hystrix有兩個(gè)隔離策略,thread和semaphore,默認(rèn)是thread
thread 通過線程數(shù)量來限制并發(fā)請求數(shù),可以提供額外的保護(hù),但有一定的延遲,一般用于網(wǎng)絡(luò)調(diào)用
semaphore 通過semaphore count來限制并發(fā)請求數(shù),適用于無網(wǎng)絡(luò)的高并發(fā)請求
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds 命令執(zhí)行超時(shí)時(shí)間
hystrix.command.default.execution.isolation.semaphore.maxConcurrentRequests 最大并發(fā)請求數(shù),默認(rèn)10,該參數(shù)當(dāng)使用ExecutionIsolationStrategy.SEMAPHORE策略時(shí)才有效。如果達(dá)到最大并發(fā)請求數(shù),請求會被拒絕。
理論上選擇semaphore size的原則和選擇thread size一致,但選用semaphore時(shí)每次執(zhí)行的單元要比較小且執(zhí)行速度快(ms級別),否則的話應(yīng)該用thread。
@RestController
public class MovieController {
@Autowired
private RestTemplate restTemplate;
@GetMapping("/movie/{id}")
@HystrixCommand(fallbackMethod = "findByIdFallback", commandProperties = @HystrixProperty(name = "execution.isolation.strategy", value = "SEMAPHORE"))
public User findById(@PathVariable Long id) {
return this.restTemplate.getForObject("http://microservice-provider-user/simple/" + id, User.class);
}
public User findByIdFallback(Long id) {
User user = new User();
user.setId(0L);
return user;
}
}
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
public class ConsumerMovieFeignApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerMovieFeignApplication.class, args);
}
}
@FeignClient(name = "microservice-provider-user", fallback = HystrixClientFallback.class)
public interface UserFeignClient {
@RequestLine("GET /simple/{id}")
public User findById(@Param("id") Long id);
}
@Component
public class HystrixClientFallback implements UserFeignClient {
@Override
public User findById(Long id) {
User user = new User();
user.setId(0L);
return user;
}
}
明確問題1:進(jìn)入斷路器定義失敗方法情況
(1):run()方法拋出非HystrixBadRequestException異常。
(2):run()方法調(diào)用超時(shí)
(3):熔斷器開啟攔截調(diào)用
(4):線程池/隊(duì)列/信號量是否跑滿
明確問題2:進(jìn)入失敗方法不一定短路器短路開關(guān)打開。
短路器短路開關(guān)有一定的條件,要滿足再某個(gè)時(shí)間點(diǎn)失敗次數(shù)達(dá)到一定數(shù)量??梢酝ㄟ^查看/hearth 來查看斷路器狀態(tài)。
斷路器有三個(gè)狀態(tài),打開、關(guān)閉和半開。半開是指斷路器打開后過一段時(shí)間它會嘗試置為半開狀態(tài),即大部分請求還是短路,允許很少一部分流量請求,如果請求失敗了,則斷路器狀態(tài)則變成打開,如果請求成功了,則斷路器狀態(tài)變成關(guān)閉。
7.服務(wù)網(wǎng)關(guān)zuul
Zuul 在云平臺上提供動態(tài)路由,監(jiān)控,彈性,安全等邊緣服務(wù)的框架。
優(yōu)點(diǎn):
- 易于監(jiān)控。可在微服務(wù)網(wǎng)關(guān)收集監(jiān)控?cái)?shù)據(jù)并將其推送到外部系統(tǒng)進(jìn)行分析。
- 易于認(rèn)證。可在微服務(wù)網(wǎng)關(guān)上進(jìn)行認(rèn)證。然后再將請求轉(zhuǎn)發(fā)到后端的微服務(wù),而無須在每個(gè)微服務(wù)中進(jìn)行認(rèn)證。
- 減少了客戶端與各個(gè)微服務(wù)之間的交互次數(shù)。
實(shí)現(xiàn)方式如下 ZuulApplication.java 入口類
@EnableZuulProxy
@SpringBootApplication
public class ZuulApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulApplication.class, args);
}
}
application.yml 配置類
zuul:
prefix: /api
routes:
aaa:
path: /user/**
serviceId: microservice-provider-user
legacy:
path: /**
serviceId: microservice-provider-user_old
PS:zuul.prefix表示所有走網(wǎng)關(guān)轉(zhuǎn)發(fā)的路由必須要有api前綴,zuul默認(rèn)支持serviceId/serviceName(除非設(shè)置zuul.ignoredServices=''),同時(shí)也可以設(shè)置path匹配規(guī)則,legacy表示當(dāng)所有路由規(guī)則都不匹配才生效*
zuul同樣支持?jǐn)嗦菲鞴δ?br> 配置一個(gè)FallbackProvider即可
@Component
public class MyFallbackProvider implements ZuulFallbackProvider {
@Override
public String getRoute() {
return "microservice-provider-user";
}
@Override
public ClientHttpResponse fallbackResponse() {
return new ClientHttpResponse() {
@Override
public HttpStatus getStatusCode() throws IOException {
return HttpStatus.OK;
}
@Override
public int getRawStatusCode() throws IOException {
return 200;
}
@Override
public String getStatusText() throws IOException {
return "OK";
}
@Override
public void close() {
}
@Override
public InputStream getBody() throws IOException {
String json = "{\"success\":false, \"msg\":\"error\"}";
return new ByteArrayInputStream(json.getBytes());
}
@Override
public HttpHeaders getHeaders() {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
return headers;
}
};
}
}
8.spring cloud config 分布式配置中心
spring cloud config作為配置中心優(yōu)勢?
- 集中管理
- 不同環(huán)境不同配置
- 運(yùn)行期間動態(tài)調(diào)整配置
- 自動刷新
配置服務(wù)端 數(shù)據(jù)repo存放在git上,ConfigServer搭建如下
ConfigServerApplication 入口類
@SpringBootApplication
@EnableConfigServer
public class ConfigServerApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigServerApplication.class, args);
}
}
application.yml配置類
spring:
cloud:
config:
server:
git:
uri: https://gitee.com/habib/{application} #通配符,匹配git habib這個(gè)目錄下所有工程
server:
port: 8080
配置客戶端 ConfigClient
bootstrap.yml
bootstrap.yml
spring:
cloud:
config:
uri: http://localhost:8080 #config server訪問地址 如果config server配置登錄校驗(yàn),這邊可以這樣寫 http://user:password123@localhost:8080,或者username和password寫在下面
profile: dev #支持多環(huán)境 即對應(yīng)application-dev.yml或者 application-dev.properties
label: master #默認(rèn)就是master
application:
name: simple #對應(yīng)https://gitee.com/habib/{application} 這個(gè)地址的application
PS:客戶端加載順序:bootstrap.yml -> 加載服務(wù)端配置文件 -> application.yml,所以要注意讀取部分的配置要放在bootstrap.yml
9.spring cloud bus
整合了java的事件處理機(jī)制和消息中間件,可以簡單的認(rèn)為是一個(gè)輕量級的消息總線。使用時(shí)它默認(rèn)需要在一個(gè)消息中間件基礎(chǔ)上,比如說rabbitmq、kafka。
以rabbitmq為例子
bootstrap.yml 配置類
spring:
cloud:
config:
uri: http://localhost:8080
profile: dev
label: master
application:
name: simple
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest
PS:config-server 和 config-client最好都添加上如上的消息配置。執(zhí)行 http://config-server/bus/refresh 即可。 這個(gè)觸發(fā)事件可以通過github webhook來觸發(fā)。