簡明Spring Cloud 實(shí)戰(zhàn)入門

簡介

Spring Cloud是一套完整的微服務(wù)解決方案承疲,是一系列不同功能的微服務(wù)框架的集合府瞄。
Spring Cloud基于Spring Boot敷矫,簡化了分布式系統(tǒng)的開發(fā)础芍,集成了服務(wù)發(fā)現(xiàn)灸叼、配置管理神汹、消息總線、負(fù)載均衡怜姿、斷路器慎冤、數(shù)據(jù)監(jiān)控等各種服務(wù)治理能力。比如sleuth提供了全鏈路追蹤能力沧卢,Netflix套件提供了hystrix熔斷器蚁堤、zuul網(wǎng)關(guān)等眾多的治理組件。config組件提供了動(dòng)態(tài)配置能力,bus組件支持使用RabbitMQ披诗、kafka撬即、Activemq等消息隊(duì)列,實(shí)現(xiàn)分布式服務(wù)之間的事件通信呈队。

Spring Cloud高可用架構(gòu)

基礎(chǔ)組件1-Eureka server

這個(gè)是微服務(wù)的通訊錄(注冊(cè)中心)剥槐,既然是微服務(wù),那么在調(diào)用別的微服務(wù)的時(shí)候肯定需要其他微服務(wù)的地址宪摧、端口等信息粒竖,而這些信息都有Eureka Server來管理。 而Eureka Server的啟動(dòng)比較簡單几于,作為一個(gè)Spring boot類型的項(xiàng)目來啟動(dòng)蕊苗。 對(duì)于習(xí)慣了Dubbo開發(fā)的同學(xué)來說,在使用Spring Cloud時(shí)遇到的第一個(gè)不習(xí)慣的地方就是沿彭,注冊(cè)中心Eureka不是一個(gè)像Zookeeper那樣獨(dú)立運(yùn)行的中間件朽砰,而是可以用Springboot來啟動(dòng)運(yùn)行,有點(diǎn)類似于嵌入式的tomcat喉刘,這是Spring Cloud體系的一大特點(diǎn)瞧柔,除了注冊(cè)中心還有網(wǎng)關(guān)Zuul也是類似的啟動(dòng)方式。

基本的server

  1. 引入pom文件
<dependency>
 <groupId>org.springframework.cloud</groupId>
 <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
 </dependency>
  1. 增加啟動(dòng)類注解
@EnableEurekaServer
@SpringBootApplication
public class PlatformEurekaApplication {
 public static void main(String[] args) {
 SpringApplication.run(PlatformEurekaApplication.class, args);
 }
}
  1. 配置yml文件
  • 默認(rèn)yml
server:
 port: 7001 #(eureka 默認(rèn)端口為:8761)

spring:
 application:
 name: platform-eureka #服務(wù)名

eureka:
 instance:
 #服務(wù)失效時(shí)間睦裳,Eureka多長時(shí)間沒收到服務(wù)的renew操作造锅,就剔除該服務(wù),默認(rèn)90秒
 leaseExpirationDurationInSeconds: 15
 ip-address: ${spring.cloud.client.ip-address}
 hostname: ${eureka.instance.ip-address}
 instanceId: ${eureka.instance.ip-address}:${server.port}
 preferIpAddress: true     #將IP注冊(cè)到Eureka Server上
 client:
 #是否注冊(cè)自身到eureka服務(wù)器廉邑,因?yàn)楫?dāng)前這個(gè)應(yīng)用就是eureka服務(wù)器备绽,沒必要注冊(cè)自身,所以這里是false
 registerWithEureka: false
 fetchRegistry: false      #表示是否從eureka服務(wù)器獲取注冊(cè)信息
 serviceUrl:
 #是設(shè)置eureka服務(wù)器所在的地址鬓催,查詢服務(wù)和注冊(cè)服務(wù)都需要依賴這個(gè)地址(注意:地址最后面的 /eureka/ 這個(gè)是固定值)
 defaultZone: http://${eureka.instance.ip-address}:${server.port}/eureka/
 server:
 #設(shè)為false,關(guān)閉自我保護(hù)恨锚,開發(fā)測試環(huán)境需要頻繁啟動(dòng)注冊(cè)實(shí)例宇驾,需要關(guān)閉自我保護(hù)功能,以免請(qǐng)求跑到舊實(shí)例中猴伶,生成環(huán)境需要開啟自我保護(hù)功能
 enableSelfPreservation: false
 #eureka server清理無效節(jié)點(diǎn)的時(shí)間間隔课舍,默認(rèn)60000毫秒,即60秒
 eviction-interval-timer-in-ms: 5000
 # 續(xù)期時(shí)間他挎,即掃描失效服務(wù)的間隔時(shí)間(缺省為60*1000ms)
 eureka.server.evictionIntervalTimerInMs: 20000
  • dev.yml
eureka:
 instance:
 ip-address: 127.0.0.1

增加監(jiān)控

可以對(duì)每個(gè)服務(wù)增加安全監(jiān)控筝尾,利用形如http://localhost:7001/Actuator 的訪問方式進(jìn)行訪問。

  1. 修改pom
 <dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-actuator</artifactId>
 </dependency>
  1. 修改yml
# Spring Cloud應(yīng)用監(jiān)控與管理Actuator
management:
 endpoints:
 enabled-by-default: true
 web:
 exposure:
 include: "*"

增加安全認(rèn)證

  1. 引入安全模塊的pom
<dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-security</artifactId>
 </dependency>
  1. 修改yml文件
spring:
 security:
 basic:
 enabled: true # 開啟基于HTTP basic的認(rèn)證
 user:
 name: admin  # 配置登錄的賬號(hào)是admin
 password: 123456 #配置登錄的密碼是 123456
  1. 增加一個(gè)java類配置登錄模式
@EnableWebSecurity
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
 @Override
 protected void configure(HttpSecurity http) throws Exception {
 http.csrf().disable(); // 關(guān)閉csrf
 http.authorizeRequests().anyRequest().authenticated().and().httpBasic(); // 開啟認(rèn)證
 }
}

基礎(chǔ)組件2-服務(wù)提供者

服務(wù)提供者要作為Eureka的客戶端在注冊(cè)中心注冊(cè)為服務(wù)提供者办桨,這里重點(diǎn)是注冊(cè)自己的名字和服務(wù)地址筹淫。

  1. 引入pom
<dependency>
 <groupId>org.springframework.cloud</groupId>
 <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
 </dependency>
  1. 默認(rèn)yml
server:
 port: ${server.port}

spring:
 application:
 name: platform-user

eureka:
 instance:
 hostname: ${eureka.instance.ip-address}
 instanceId: ${eureka.instance.ip-address}:${server.port}
 prefer-ip-address: true
 registry.default-open-for-traffic-count: ${eureka.instance.registry.default-open-for-traffic-count}
 registry.expected-number-of-renews-per-min: ${eureka.instance.registry.expected-number-of-renews-per-min}
 client:
 enabled: true
 serviceUrl:
 defaultZone: ${eureka.client.serviceUrl.defaultZone}
  1. dev.yml
server:
 port: 7081

spring:
 profiles: dev 
eureka:
 instance:
 ip-address: 127.0.0.1
 registry.default-open-for-traffic-count: 1
 registry.expected-number-of-renews-per-min: 1 
 client:
 serviceUrl:
 defaultZone: http://admin:123456@127.0.0.1:7001/eureka/

management:
 endpoints:
 enabled-by-default: true
 web:
 exposure:
 include: "*"
  1. 增加注解
@EnableDiscoveryClient
@SpringBootApplication
@EnableTransactionManagement
@MapperScan("com.itcast.mapper")
public class UserApplication {
 public static void main(String[] args) {
 SpringApplication.run(UserApplication.class, args);
 }
}

注意:

從Spring Cloud Edgware開始,@EnableDiscoveryClient 或@EnableEurekaClient 可省略呢撞。只需加上相關(guān)依賴损姜,并進(jìn)行相應(yīng)配置饰剥,即可將微服務(wù)注冊(cè)到服務(wù)發(fā)現(xiàn)組件上。

基礎(chǔ)組件3-服務(wù)消費(fèi)者

通過注冊(cè)中心查找自己需要的服務(wù)地址摧阅,就想提供名字查電話一樣汰蓉。 與服務(wù)提供者的編寫方式基本一致。為了后面增加網(wǎng)關(guān)支持的方便棒卷,這里面的服務(wù)消費(fèi)者本身也是服務(wù)提供者顾孽。

基礎(chǔ)組件4-Feign

在消費(fèi)者中使用,簡化Http API的調(diào)用比规,使消費(fèi)者調(diào)用服務(wù)提供者就想調(diào)用本地接口一樣方便若厚。 Spring Cloud對(duì)原生Feign進(jìn)行了整合。

  1. 引入pom
<dependency>
 <groupId>org.springframework.cloud</groupId>
 <artifactId>spring-cloud-starter-openfeign</artifactId>
 </dependency>
  1. 增加注解
@EnableFeignClients
  1. 按照服務(wù)提供者對(duì)應(yīng)服務(wù)協(xié)議編寫對(duì)應(yīng)接口(不用實(shí)現(xiàn))
@FeignClient(name = "PLATFORM-USER")
public interface UserService {
 /***
 * 用戶信息查詢
 */
 @GetMapping(value = "/user/search")
 public String searchUser(@RequestParam("userName") String userName);
}
  1. 像本地方法一樣調(diào)用
 @Autowired
 private UserService userService;

 // 業(yè)務(wù)代碼
 strJson = userService.searchUser(orderInfo.getUserName());

基礎(chǔ)組件5-Hystrix

Feign中已經(jīng)支持Hystrix苞俘,當(dāng)服務(wù)提供者出現(xiàn)故障時(shí)盹沈,Hystrix會(huì)自動(dòng)切換到備胎方案。

  1. 增加fallback參數(shù)
@FeignClient(name = "PLATFORM-USER", fallback = UserServiceFallbackImpl.class)
  1. 實(shí)現(xiàn)備胎代碼
@Component
//@Service
public class UserServiceFallbackImpl implements UserService {

 private static final Logger LOGGER = LoggerFactory.getLogger(UserServiceFallbackImpl.class);

 @Override
 public String searchUser(String userName){
 LOGGER.error("用戶信息查詢接口調(diào)用異常:searchUser");
 return JsonUtils.toText(ResponseUtils.failure("調(diào)用用戶信息查詢接口服務(wù)異常吃谣!"));
 }
}

基礎(chǔ)組件6-Zuul

內(nèi)容簡介

Zuul網(wǎng)關(guān)的功能和工作機(jī)制乞封、結(jié)合代碼介紹如何使用Zuul構(gòu)建一個(gè)簡單的網(wǎng)關(guān)、介紹Zuul的路由配置方式岗憋、了解Filter工作原理并實(shí)現(xiàn)一些擴(kuò)展功能肃晚。

Zuul網(wǎng)關(guān)簡介

Zuul是Spring Cloud全家桶中的微服務(wù)API網(wǎng)關(guān)。 所有從設(shè)備或網(wǎng)站來的請(qǐng)求都會(huì)經(jīng)過Zuul到達(dá)后端的Netflix應(yīng)用程序仔戈。作為一個(gè)邊界性質(zhì)的應(yīng)用程序关串,Zuul提供了動(dòng)態(tài)路由、監(jiān)控监徘、彈性負(fù)載和安全功能晋修。Zuul底層利用各種filter實(shí)現(xiàn)如下功能:

  • 認(rèn)證和安全 識(shí)別每個(gè)需要認(rèn)證的資源,拒絕不符合要求的請(qǐng)求凰盔。

  • 性能監(jiān)測 在服務(wù)邊界追蹤并統(tǒng)計(jì)數(shù)據(jù)墓卦,提供精確的生產(chǎn)視圖。

  • 動(dòng)態(tài)路由 根據(jù)需要將請(qǐng)求動(dòng)態(tài)路由到后端集群户敬。

  • 壓力測試 逐漸增加對(duì)集群的流量以了解其性能落剪。

  • 負(fù)載卸載 預(yù)先為每種類型的請(qǐng)求分配容量,當(dāng)請(qǐng)求超過容量時(shí)自動(dòng)丟棄尿庐。

  • 靜態(tài)資源處理 直接在邊界返回某些響應(yīng)忠怖。

編寫一個(gè)Zuul網(wǎng)關(guān)

1、新建一個(gè)zuul-demo模塊抄瑟,在依賴項(xiàng)處添加【Cloud Discovery->Eureka Discovery和Cloud Rouing->Zuul】凡泣。

 <dependency>
 <groupId>org.springframework.cloud</groupId>
 <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
 </dependency>

2、修改入口類,增加EnableZuulProxy注解

@SpringBootApplication
@EnableZuulProxy
public class ZuulDemoApplication { 
 public static void main(String[] args) {
 SpringApplication.run(ZuulDemoApplication .class, args);
 }
}
  1. 修改appliation.yml
server:
 port: 7000
spring:
 application:
 name: zuul-demo
eureka:
 client:
 service-url:
 defaultZone: http://localhost:7001/eureka/
 instance:
 prefer-ip-address: true
 #將IP注冊(cè)到Eureka Server上问麸,而如果不配置就是機(jī)器的主機(jī)名往衷。

4、啟動(dòng)Eureka Server严卖、orderservice和 Zuul席舍,在瀏覽器中輸入http://localhost:7000/orderservice/order/pay 獲取返回結(jié)果。

從上面的例子中的地址可以看出來默認(rèn)Zuul的路由方式是:http://ZUULHOST:ZUULPORT/serviceId/**哮笆。

如果啟動(dòng)多個(gè)orderservice可以發(fā)現(xiàn)Zuul里面還內(nèi)置了Ribbon的負(fù)載均衡功能来颤。

Filter工作原理

Zuul中的Filter

Zuul是圍繞一系列Filter展開的,這些Filter在整個(gè)HTTP請(qǐng)求過程中執(zhí)行一連串的操作稠肘。 Zuul Filter有以下幾個(gè)特征: Type:用以表示路由過程中的階段(內(nèi)置包含PRE福铅、ROUTING、POST和ERROR) Execution Order:表示相同Type的Filter的執(zhí)行順序 Criteria:執(zhí)行條件 Action:執(zhí)行體

Zuul請(qǐng)求生命周期

一圖勝千言项阴,下面通過官方的一張圖來了解Zuul請(qǐng)求的生命周期滑黔。
image.png

自定義一個(gè)Filter實(shí)現(xiàn)token驗(yàn)證

  1. 添加一個(gè)AuthZuulFilter
public class AuthZuulFilter extends ZuulFilter {
 // 日志輸出器
 private final static Logger LOGGER = LoggerFactory.getLogger(AuthZuulFilter.class);

 @Value("${server.port}")
 private String serverPort;

 //四種類型:pre,routing,error,post
 @Override
 public String filterType() {
 return FilterConstants.PRE_TYPE;
 }

 //自定義過濾器執(zhí)行的順序,數(shù)值越大越靠后執(zhí)行环揽,越小就越先執(zhí)行
 @Override
 public int filterOrder() {
 return FilterConstants.PRE_DECORATION_FILTER_ORDER;
 }

 //控制過濾器生效不生效略荡,可以在里面寫一串邏輯來控制
 @Override
 public boolean shouldFilter() {
 return true;
 }

 //執(zhí)行過濾邏輯
 @Override
 public Object run() throws ZuulException {
 RequestContext ctx = RequestContext.getCurrentContext();
 HttpServletRequest request = ctx.getRequest();
 //從請(qǐng)求頭中獲取token信息
 String userToken = request.getHeader("userToken");
 LOGGER.debug("網(wǎng)關(guān)端口:" + serverPort);
 if (StringUtils.isEmpty(userToken)) {
 //設(shè)置為false就不會(huì)繼續(xù)執(zhí)行服務(wù)代碼
 ctx.setSendZuulResponse(false);
 //設(shè)置狀態(tài)碼
 ctx.setResponseStatusCode(401);
 //設(shè)置相應(yīng)信息
 ctx.setResponseBody("userToken is null");
 return null;
 }
 return null;
 }
}
  1. 修改啟動(dòng)程序,添加Filter注入:
 @Bean
 public AuthZuulFilter authZuulFilter() {
 AuthZuulFilter filter = new AuthZuulFilter();
 return filter;
 }

路由配置

Zuul提供了一套簡單且強(qiáng)大路由配置策略歉胶,利用路由配置我們可以完成對(duì)微服務(wù)和URL更精確的控制

  1. 重寫指定微服務(wù)的訪問路徑:
zuul:
 routes:
 platform-order: /orderservice/**

這表示將rest-demo微服務(wù)的地址映射到/rest/**路徑汛兜。

  1. 忽略指定微服務(wù):
zuul:
 ignored-services: xxx-service

使用“*”可忽略所有微服務(wù),多個(gè)指定微服務(wù)以半角逗號(hào)分隔通今。

3粥谬、忽略所有微服務(wù),只路由指定微服務(wù):

zuul:
 ignored-services: *
 routes:
 platform-order: /orderservice/**

4辫塌、路由別名:

zuul:
 routes:
 platform-order: #路由別名漏策,無其他意義,與例1效果一致
 service-id: platform-order
 path: /orderservice/**

5臼氨、指定path和URL

zuul:
 routes:
 platform-order:
 url: http://localhost:8000/
 path: /orderservice/** 

此例將http://ZUULHOST:ZUULPORT/rest/映射到http://localhost:8000/哟玷,同時(shí)由于并非用service-id定位服務(wù),所以也無法使用負(fù)載均衡功能一也。

限流

#服務(wù)限流
zuul:
 ratelimit:
 enabled: true
 repository: REDIS         #使用redis存儲(chǔ),一定要大寫
 policies:
 platform-order: #針對(duì)platform-order服務(wù)限流
 limit: 10              #多少個(gè)請(qǐng)求(次數(shù))
 refreshInterval: 60     #測試客戶端如果60秒內(nèi)請(qǐng)求超過10次喉脖,服務(wù)端就拋出429異常(秒)
 type:
 - ORIGIN             #可選 限流方式: url通過請(qǐng)求路徑區(qū)分  origin通過客戶端IP地址區(qū)分  user是通過登錄用戶名進(jìn)行區(qū)分椰苟,也包括匿名用戶
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市树叽,隨后出現(xiàn)的幾起案子舆蝴,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,544評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件洁仗,死亡現(xiàn)場離奇詭異层皱,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)赠潦,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門叫胖,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人她奥,你說我怎么就攤上這事瓮增。” “怎么了哩俭?”我有些...
    開封第一講書人閱讀 162,764評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵绷跑,是天一觀的道長。 經(jīng)常有香客問我凡资,道長砸捏,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,193評(píng)論 1 292
  • 正文 為了忘掉前任隙赁,我火速辦了婚禮垦藏,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘鸳谜。我一直安慰自己膝藕,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,216評(píng)論 6 388
  • 文/花漫 我一把揭開白布咐扭。 她就那樣靜靜地躺著芭挽,像睡著了一般。 火紅的嫁衣襯著肌膚如雪蝗肪。 梳的紋絲不亂的頭發(fā)上袜爪,一...
    開封第一講書人閱讀 51,182評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音薛闪,去河邊找鬼辛馆。 笑死,一個(gè)胖子當(dāng)著我的面吹牛豁延,可吹牛的內(nèi)容都是我干的昙篙。 我是一名探鬼主播,決...
    沈念sama閱讀 40,063評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼诱咏,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼苔可!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起袋狞,我...
    開封第一講書人閱讀 38,917評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤焚辅,失蹤者是張志新(化名)和其女友劉穎映屋,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體同蜻,經(jīng)...
    沈念sama閱讀 45,329評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡棚点,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,543評(píng)論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了湾蔓。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片瘫析。...
    茶點(diǎn)故事閱讀 39,722評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖卵蛉,靈堂內(nèi)的尸體忽然破棺而出颁股,到底是詐尸還是另有隱情,我是刑警寧澤傻丝,帶...
    沈念sama閱讀 35,425評(píng)論 5 343
  • 正文 年R本政府宣布甘有,位于F島的核電站,受9級(jí)特大地震影響葡缰,放射性物質(zhì)發(fā)生泄漏亏掀。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,019評(píng)論 3 326
  • 文/蒙蒙 一泛释、第九天 我趴在偏房一處隱蔽的房頂上張望滤愕。 院中可真熱鬧,春花似錦怜校、人聲如沸间影。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,671評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至籽前,卻和暖如春裙顽,著一層夾襖步出監(jiān)牢的瞬間付燥,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,825評(píng)論 1 269
  • 我被黑心中介騙來泰國打工愈犹, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留键科,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,729評(píng)論 2 368
  • 正文 我出身青樓漩怎,卻偏偏與公主長得像勋颖,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子勋锤,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,614評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容