<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}
server:
port: 8761
eureka:
instance:
hostname: localhost
client:
registerWithEureka: false
fetchRegistry: false
serviceUrl:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
#是否將自己註冊到Eureka服務中,預設為true(由於當前就是eureka服務,固設定為false)
registerWithEureka: false
#設定是否從eureka服務上獲取註冊資訊,預設為true(由於當前就是eureka服務,固設定為false)
fetchRegistry: false
server:
# 是否開啟自我保護模式(自我保護模式,當eureka在一定時間內沒有接受到某個微服務範例的心跳包,預設90S會登出該範例),
# 一旦進入自我保護模式,若短時間內丟失大量使用者端,eureka也會保護登入檔的資訊,不再登出
enable-self-preservation: false
# 清理間隔。預設為60000ms
eviction-interval-timer-in-ms: 5000
<!-- Eureka使用者端 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency
2.新增 @EnableDiscoveryClient 來開啟Eureka使用者端功能
@SpringBootApplication
@EnableDiscoveryClient
public class SpringDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SpringDemoApplication.class, args);
}
}
3.編寫組態檔
eureka:
instance:
#在呼叫服務的時候就已經變成ip地址;需要注意的是:不是在eureka中的控制檯服務範例狀態顯示。
ip-address: 127.0.0.1
prefer-ip-address: true #使用ip註冊
client:
# 是否開啟健康檢查
healthcheck:
enabled: true
# 叢集url
service-url:
defaultZone: http://127.0.0.1:8761/eureka
消費應用從註冊中心獲取服務列表,從而得知每個服務方的資訊,知道去哪裡呼叫服務方
在註冊服務完成以後,服務提供者會維持一個心跳(定時向EurekaServer發起Rest請求),有兩個重要引數可以修改服務續約的行為
eureka:
instance:
#服務失效時間,預設值90秒
lease-expiration-duration-in-seconds: 90
#服務續約(renew)的間隔,預設為30秒
lease-renewal-interval-in-seconds: 30
也就是說,預設情況下每隔30秒服務會向註冊中心傳送一次心跳,證明自己還活著。如果超過90秒沒有傳送心跳,EurekaServer就會認為該服務宕機,會定時(eureka.server.eviction-interval-timer-in-ms設定的時間)從服務列表中剔除
服務註冊中心在啟動時會建立一個定時任務,預設每隔一段時間(預設為60秒)將當前清單中超時(預設為90秒)沒有續約的服務剔除。
Eureka Server在執行期間,會統計心跳失敗的比例在15分鐘之內是否低於85%,如果出現低於的情況,Eureka Server會將當前的範例註冊資訊保護起來,不再刪除服務登入檔中的資料(也就是不會登出任何微服務)
Consul強一致性(CP),Eureka保證高可用和最終一致性(AP)
Consul使用Go語言,Eureka使用Java語言
Consul 不同於 Eureka 需要單獨安裝,官網:https://www.consul.io/downloads
服務提供者
<!--SpringCloud提供的基於Consul的服務發現-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
<!--actuator用於心跳檢查-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
spring:
application:
name: consul-provider
####consul註冊中心地址
cloud:
consul:
host: localhost
port: 8500
discovery:
service-name: ${spring.application.name} #註冊中心名字與spring的應用名稱一致
#-----------------------------相關設定----------------------------|
#是否註冊
register: true
#範例ID
instance-id: ${spring.application.name}-1
#服務範例名稱
service-name: ${spring.application.name}
#服務範例埠
port: ${server.port}
#健康檢查路徑
healthCheckPath: /actuator/health
#健康檢查時間間隔
healthCheckInterval: 15s
#開啟ip地址註冊
prefer-ip-address: true
#範例的請求ip
ip-address: ${spring.cloud.client.ip-address}
服務消費者
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
spring:
application:
name: consul-provider
####consul註冊中心地址
cloud:
consul:
host: localhost
port: 8500
discovery:
#設定不需要註冊到Consul中
register: false
Ribbon是 Netflix 釋出的一個負載均衡器,有助於控制 HTTP 和 TCP使用者端行為。Ribbon自動的從註冊中心中獲取服務提供者的
列表資訊,並基於內建的負載均衡演演算法,請求服務。
先編寫請求的介面
@Value("${spring.cloud.client.ip-address}")
private String ip;
@Value("${server.port}")
private String port;
@ResponseBody
@RequestMapping(value = "backInfo", method = RequestMethod.GET)
public String backInfo() {
return "呼叫的是" + ip + "埠是:" + port;
}
我們假設搭建兩臺服務提供者,埠分別為:8090和8091,將已有服務的設定更改為
server:
#的${}表示在jvm啟動時候若能找到對應port則使用,若無則使用後面的預設值
port: ${port:8090}
另外一臺在啟動的時候可以指定埠port,在如下介面中的 VM options 中設定 -Dport=10087
因為Eureka中已經整合了Ribbon,所以我們無需引入新的依賴。直接修改消費者的啟動類
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
@Autowired
private RestTemplate restTemplate;
@ResponseBody
@RequestMapping(value = "getIpAndPort", method = RequestMethod.GET)
public String printInfo() {
String url = "http://springbootdemo/backInfo";
return restTemplate.getForObject(url, String.class);
}
Ribbon預設的負載均衡策略是輪詢,Ribbon內建了多種負載均衡策略
修改負載均衡規則的設定:
springbootdemo:
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
#格式是: {服務名稱}.ribbon.NFLoadBalancerRuleClassName
策略選擇:
如果每個機器設定一樣,則建議不修改策略 (推薦)
如果部分機器設定強,則可以改為 WeightedResponseTimeRule
RibbonAutoConfifiguration->LoadBalancerAutoConfiguration->LoadBalancerInterceptor
Feign是一種宣告式、模板化的HTTP使用者端。
1.在消費者引入依賴
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
2.啟動類開啟feign支援
@SpringBootApplication
@EnableDiscoveryClient
@ComponentScan(basePackages = "com.jyd0124.consume_server.*")
@EnableFeignClients
public class ConsumeServerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumeServerApplication.class, args);
}
@Bean
@LoadBalanced
public RestTemplate returnBean() {
return new RestTemplate();
}
}
3.建立介面和定義需要遠端呼叫的方法
@FeignClient("springbootdemo")
public interface SpringBootFeign {
@RequestMapping(value = "/person", method = RequestMethod.GET)
public Map<String, Object> getPerson(@RequestParam(value = "id") int id);
}
4.呼叫服務。
@Autowired
private SpringBootFeign springBootFeign;
@ResponseBody
@RequestMapping(value = "/person/{id}", method = RequestMethod.GET)
public Map<String,Object> printInfo(@PathVariable("id") int id) {
return springBootFeign.getPerson(id);
}
5.存取,測試結果
Feign中本身已經整合了Ribbon依賴和自動設定,因此我們不需要額外引入依賴,也不需要再註冊RestTemplate 物件,可以通過 ribbon.xx 來進行全域性設定。也可以通過 服務名.ribbon.xx 來對指定服務設定。
feign:
client:
config:
feignName: ##定義FeginClient的名稱
connectTimeout: 5000 # 建立連結的超時時長
readTimeout: 5000 # 讀取超時時長
# 設定Feign的紀錄檔級別,相當於程式碼設定方式中的Logger
loggerLevel: full
# Feign的錯誤解碼器,相當於程式碼設定方式中的ErrorDecoder
errorDecoder: com.example.SimpleErrorDecoder
# 設定重試,相當於程式碼設定方式中的Retryer
retryer: com.example.SimpleRetryer
# 設定攔截器,相當於程式碼設定方式中的
RequestInterceptor requestInterceptors:
- com.example.FooRequestInterceptor
- com.example.BarRequestInterceptor
#設定熔斷不處理404異常
decode404: false
請求壓縮: 支援對請求和響應進行GZIP壓縮,以減少通訊過程中的效能損耗
#請求壓縮
feign:
compression:
request:
enabled: true # 開啟請求壓縮
response:
enabled: true # 開啟響應壓縮
Feign預設也有對Hystrix的整合
feign:
hystrix:
enabled: true # 開啟Feign的熔斷功能
預設情況下Feign的紀錄檔是沒有開啟的。
feign:
client:
config:
feignName:
loggerLevel: full
#NONE【效能最佳,適用於生產】:不記錄任何紀錄檔(預設值)
#BASIC【適用於生產環境追蹤問題】:僅記錄請求方法、URL、響應狀態程式碼以及執行時間
#HEADERS:記錄BASIC級別的基礎上,記錄請求和響應的header。
#FULL【比較適用於開發及測試環境定位問題】:記錄請求和響應的header、body和後設資料
在微服務架構中,根據業務來拆分成一個個的服務,服務與服務之間可以相互呼叫(RPC),在Spring Cloud可以用RestTemplate+Ribbon和Feign來呼叫。為了保證其高可用,單個服務通常會叢集部署。由於網路原因或者自身的原因,服務並不能保證100%可用,如果單個服務出現問題,呼叫這個服務就會出現執行緒阻塞,此時若有大量的請求湧入,Servlet容器的執行緒資源會被消耗完畢,導致服務癱瘓。服務與服務之間的依賴性,故障會傳播,會對整個微服務系統造成災難性的嚴重後果,這就是服務故障的「雪崩」效應。
Hystrix的熔斷狀態機模型:
hystrix:
command:
default:
execution.isolation.thread.timeoutInMilliseconds: 2000
circuitBreaker:
errorThresholdPercentage: 50 # 觸發熔斷錯誤比例閾值,預設值50%
sleepWindowInMilliseconds: 10000 # 熔斷後休眠時長,預設值5秒
requestVolumeThreshold: 10 # 觸發熔斷的最小請求次數,預設20
#設定項可以參考 HystrixCommandProperties 類
服務降級
通過HystrixdeCommand的fallback實現服務降級。
服務隔離
-執行緒池隔離策略
- 號誌隔離策略
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
@SpringBootApplication
@EnableDiscoveryClient
@EnableHystrix
public class ConsumeServerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumeServerApplication.class, args);
}
@Bean
@LoadBalanced
public RestTemplate returnBean() {
return new RestTemplate();
}
}
@ResponseBody
@RequestMapping(value = "/info", method = RequestMethod.GET)
@HystrixCommand(fallbackMethod = "backErrorInfo")
public String printInfo() {
String url = "http://springbootdemo/backInfo";
return restTemplate.getForObject(url, String.class);
}
public String backErrorInfo(){
return "sorry,error";
}
SpringCloud Fegin預設已為Feign整合了hystrix
feign:
hystrix:
enabled: true # 開啟Feign的熔斷功能
@Component
public class HystrixMethod implements SpringBootFeign {
@Override
public Map<String, Object> getPerson(int id) {
Map<String, Object> map = new HashMap<>();
map.put("code", 500);
map.put("msg", "sorry,error");
return map;
}
}
@FeignClient(name = "springbootdemo", fallback = HystrixMethod.class)
public interface SpringBootFeign {
@RequestMapping(value = "/person", method = RequestMethod.GET)
public Map<String, Object> getPerson(@RequestParam(value = "id") int id);
}
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>
</dependency>
@SpringBootApplication
@EnableDiscoveryClient
@EnableHystrix
@EnableHystrixDashboard
public class ConsumeServerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumeServerApplication.class, args);
}
@Bean
@LoadBalanced
public RestTemplate returnBean() {
return new RestTemplate();
}
}
遇到問題:https://blog.csdn.net/ghyghost/article/details/108053206
斷路器聚合監控Turbine,Turbine是一個聚合Hystrix 監控資料的工具。
Spring Cloud Gateway 是 Spring 官方基於 Spring 5.0,Spring Boot 2.0 和 Project Reactor 等技術開發的閘道器,旨在為微服務架構提供一種簡單而有效的統一的 API 路由管理方式。Spring Cloud Gateway 作為 Spring Cloud 生態系中的閘道器,目標是替代 Netflflix ZUUL,其不僅提供統一的路由方式,並且基於 Filter 鏈的方式提供了閘道器基本的功能,例如:安全,監控/埋點,和限流等。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
@SpringBootApplication
@EnableDiscoveryClient
public class GatewayServerApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayServerApplication.class, args);
}
}
server:
port: 8765
spring:
application:
# 註冊到eureka上的應用名稱
name: gateway_server
cloud:
gateway:
#由一個ID、一個目的URL、一組斷言工廠、一組Filter組成
routes:
# 路由id,可以隨意寫
- id: consume-server-route
# 代理的服務地址
uri: http://127.0.0.1:8081
# 路由斷言,可以設定對映路徑
predicates:
- Path=/person/**
eureka:
client:
# 叢集url
service-url:
defaultZone: http://127.0.0.1:8761/eureka
instance:
prefer-ip-address: true #使用ip註冊
ip-address: 127.0.0.1
instance-id: ${spring.cloud.client.ip-address}:${server.port}
spring:
application:
# 註冊到eureka上的應用名稱
name: gateway_server
cloud:
gateway:
#由一個ID、一個目的URL、一組斷言工廠、一組Filter組成
routes:
# 路由id,可以隨意寫
- id: consume-server-route
# 代理的服務地址
uri: http://127.0.0.1:8081
# 路由斷言,可以設定對映路徑
predicates:
- Path=/**
filters:
# 新增請求路徑的字首
- PrefixPath=/person
相當於PrefixPath=/personhttp://localhost:8765/180868 --》http://localhost:8765/person/180868 --》http://localhost:8081/person/180868
spring:
application:
# 註冊到eureka上的應用名稱
name: gateway_server
cloud:
gateway:
#由一個ID、一個目的URL、一組斷言工廠、一組Filter組成
routes:
# 路由id,可以隨意寫
- id: consume-server-route
# 代理的服務地址
uri: http://127.0.0.1:8081
# 路由斷言,可以設定對映路徑
predicates:
- Path=/api/person/**
filters:
# 表示過濾1個路徑,2表示兩個路徑,以此類推
- StripPrefix=1
StripPrefix=1 http://localhost:8765/api/person/180868--》http://localhost:8765/person/180868--》http://localhost:8081/person/180868
uri以 lb: //開頭(lb代表從註冊中心獲取服務),後面接的就是你需要轉發到的服務名稱
spring:
application:
# 註冊到eureka上的應用名稱
name: gateway_server
cloud:
gateway:
routes:
# 路由id,可以隨意寫
- id: consume-server-route
# 代理的服務地址
uri: lb://springbootdemo
# 路由斷言,可以設定對映路徑
predicates:
- Path=/person/**
路由轉發是直接將匹配的路由path直接拼接到對映路徑(URI)之後,那麼往往沒有那麼便利。修改application.yaml
spring:
application:
# 註冊到eureka上的應用名稱
name: gateway_server
cloud:
gateway:
routes:
# 路由id,可以隨意寫
- id: consume-server-route
# 代理的服務地址
uri: lb://springbootdemo
# 路由斷言,可以設定對映路徑
predicates:
- Path=/springbootdemo/**
filters:
- RewritePath=/springbootdemo/(?<segment>.*), /$\{segment}
請求http://localhost:8765/springbootdemo/person/180868 --》http://localhost:8765/person/180868--》http://localhost:8081/person/180868( 值得注意的是在yml檔案中 $ 要寫成 $\ )
spring:
application:
# 註冊到eureka上的應用名稱
name: gateway_server
cloud:
gateway:
discovery:
locator:
#設定路由存取方式:http://Gateway_HOST:Gateway_PORT/大寫的serviceId/**,其中微服務應用名預設大寫存取。
enabled: true
# 該設定可以將服務名改成小寫(預設為大寫)
lower-case-service-id: true
這樣,就可以通過http://localhost:8765/springbootdemo/person?id=180868存取
過濾器名稱 | 說明 |
---|---|
AddRequestHeader | 對匹配上的請求加上Header |
AddRequestParameters | 對匹配上的請求路由新增引數 |
AddResponseHeader | 對從閘道器返回的響應新增Header |
StripPrefifix | 對匹配上的請求路徑去除字首 |
設定全域性預設過濾器
spring:
cloud:
gateway:
default-filters:
- AddResponseHeader=X-Response-Default-MyName, jyd0124
其他過濾器具體可參考GatewayFilterFactory類
自定義過濾器
需求:模擬一個登入的校驗。基本邏輯:如果請求中有token引數,則認為請求有效,放行。
@Component
public class TokenFilter implements GlobalFilter, Ordered {
private final Logger logger = LoggerFactory.getLogger(TokenFilter.class);
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
//request,response都可以通過 ServerWebExchange 獲取
String token = exchange.getRequest().getQueryParams().getFirst("token");
if (StringUtils.isBlank(token)) {
logger.info("token is empty ...");
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}
//chain.filter 繼續向下遊執行
return chain.filter(exchange);
}
@Override
public int getOrder() {
//過濾器的優先順序,返回值越大級別越低
return 0;
}
}
@Configuration
public class GwCorsFilter {
/**
* 以下CorsWebFilter跨域處理也可以通過組態檔進行處理
* spring:
* cloud:
* gateway:
* globalcors:
* cors-configurations:
*/
@Bean
public CorsWebFilter corsFilter() {
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true); // 允許cookies跨域
config.addAllowedOrigin("*");// #允許向該伺服器提交請求的URI,*表示全部允許,在SpringMVC中,如果設成*,會自動轉成當前請求頭中的Origin
config.addAllowedHeader("*");// #允許存取的頭資訊,*表示全部
config.setMaxAge(18000L);// 預檢請求的快取時間(秒),即在這個時間段裡,對於相同的跨域請求不會再預檢了
config.addAllowedMethod("OPTIONS");// 允許提交請求的方法型別,*表示全部允許
config.addAllowedMethod("HEAD");
config.addAllowedMethod("GET");
config.addAllowedMethod("PUT");
config.addAllowedMethod("POST");
config.addAllowedMethod("DELETE");
config.addAllowedMethod("PATCH");
UrlBasedCorsConfigurationSource source =
new UrlBasedCorsConfigurationSource(new PathPatternParser());
source.registerCorsConfiguration("/**", config);
return new CorsWebFilter(source);
}
/**
*
*如果使用了註冊中心(如:Eureka),進行控制則需要增加如下設定
*/
@Bean
public RouteDefinitionLocator discoveryClientRouteDefinitionLocator(DiscoveryClient discoveryClient) {
return new DiscoveryClientRouteDefinitionLocator(discoveryClient,new DiscoveryLocatorProperties());
}
/**
* 以下是springboot2.0.5出現only one connection 的解決辦法
* @return
*/
@Bean
public HiddenHttpMethodFilter hiddenHttpMethodFilter() {
return new HiddenHttpMethodFilter() {
@Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
return chain.filter(exchange);
}
};
}
}
Gateway中預設已經整合了Ribbon負載均衡和Hystrix熔斷機制。但是所有的策略都是走的預設值,建議根據Ribbon和Hystrix手動進行設定。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
@SpringBootApplication
@EnableDiscoveryClient
@EnableConfigServer
public class ConfigServerApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigServerApplication.class, args);
}
}
server:
port: 8888
spring:
application:
name: config_server
cloud:
config:
server:
git:
#組態檔所在的git倉庫
uri: https://gitee.com/jyd0124/springcloudconfig.git
#組態檔分支
default-label: master
#組態檔所在根目錄
search-paths: config
#如果Git倉庫為公開倉庫,可以不填寫使用者名稱和密碼,如果是私有倉庫需要填寫
username: xxx
password: xxx
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:8761/eureka #服務註冊地址
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
刪除consume_server的application.yml 檔案(因為該檔案從設定中心獲取)
建立consume_server 的bootstrap.yml 組態檔,其內容如下
spring:
cloud:
config:
name: consume-server
# 遠端倉庫中的版本保持一致
label: master
#profile: dev
#通過ip直接存取設定中心
#uri: http://localhost:8888/
#通過eurka存取設定中心
discovery:
#設定中心服務
service-id: config-server
enabled: true
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:8761/eureka #服務註冊地址
3.啟動consume_server專案發現獲取到組態檔將服務暴露在8081埠,測試介面可成功返回資料
補充:bootstrap.yml檔案也是Spring Boot的預設組態檔,而且其載入的時間相比於application.yml更早。bootstrap.yml檔案相當於專案啟動時的引導檔案,內容相對固定。application.yml檔案是微服務的一些常規設定引數,變化比較頻繁。
前面已經完成了將微服務中的組態檔集中儲存在遠端Git倉庫,如果我們更新Git倉庫中的組態檔,那使用者微服務是否可以及時接收到新的設定資訊並更新呢?經過測試,對於Git倉庫中組態檔的修改並沒有及時更新到使用者微服務,只有重啟使用者微服務才能生效。如果想在不重啟微服務的情況下更新設定該如何實現呢? 可以使用Spring Cloud Bus來實現設定的自動更新。
Spring Cloud Bus是用輕量的訊息代理將分散式的節點連線起來,可以用於廣播組態檔的更改或者服務的監控管理。也就是訊息匯流排可以為微服務做監控,也可以實現應用程式之間相互通訊。 Spring Cloud Bus可選的訊息代理有兩種:RabbitMQ和Kafka。
具體實現可參考:https://www.fangzhipeng.com/springcloud/2018/08/08/sc-f8-bus.html
https://www.cnblogs.com/babycomeon/p/11141160.html