spring cloud2

Zuul

代碼地址:https://github.com/jedyang/springCloud

先來看一個(gè)簡單的微服務(wù)架構(gòu)圖。

6.png

用戶請求通過負(fù)載均衡nginx,網(wǎng)關(guān)路由zuul,到達(dá)服務(wù)層。服務(wù)統(tǒng)一注冊在注冊中心Eureka,配置項(xiàng)同意在配置中心管理,配置托管在git倉庫。

Zuul的主要功能是路由轉(zhuǎn)發(fā)和過濾。路由轉(zhuǎn)發(fā)可以類比spring的servlet-mapping功能。zuul默認(rèn)已集成ribbon負(fù)載均衡。

  1. 新建一個(gè)工程zuul
    創(chuàng)建時(shí)勾選web、eureka discover、zuul

  2. 代碼開啟zuul功能

     @EnableZuulProxy
     @EnableEurekaClient
     @SpringBootApplication
     public class ZuulApplication {
     
         public static void main(String[] args) {
             SpringApplication.run(ZuulApplication.class, args);
         }
     }
    

    @EnableZuulProxy注解開啟zuul功能

  3. 改造之前的服務(wù)
    在學(xué)習(xí)ribbon和feign時(shí),我在一個(gè)工程里做了演示。現(xiàn)在想要演示zuul路由分發(fā)功能,最好將之前的工程拆分一下。我這里就不拆了,通過修改端口號提供兩個(gè)服務(wù)出來。一個(gè)service-ribbon在端口8764,另一個(gè)service-feign在8765。
    這是之前的配置文件:

     eureka:
       client:
         serviceUrl:
           defaultZone: http://localhost:8761/eureka/
     server:
       port: 8764
     #  port: 8765
     # 切換端口,啟動不同服務(wù)
     spring:
       application:
         name: service-ribbon
     #    name: service-feign
    
  4. 以此啟動之前的服務(wù)
    注冊中心8761。
    服務(wù)provider在8762。(只啟動一個(gè)好了)
    服務(wù)consumer service-ribbon在8764
    服務(wù)consumer service-feign在8765

7.png
  1. 配置下zuul工程

     eureka:
       client:
         serviceUrl:
           defaultZone: http://localhost:8761/eureka/
     server:
       port: 8766
     spring:
       application:
         name: service-zuul
     zuul:
       routes:
         api-a:
           path: /api-ribbon/**
           serviceId: service-ribbon
         api-b:
           path: /api-feign/**
           serviceId: service-feign
    

    以/api-ribbon/ 開頭的請求都轉(zhuǎn)發(fā)給service-ribbon服務(wù);以/api-feign/開頭的請求都轉(zhuǎn)發(fā)給service-feign服務(wù);

  2. 測試
    瀏覽器請求http://localhost:8766/api-feign/feignHi?name=yunsheng
    返回hi yunsheng,i am from port:8762證明zuul將請求轉(zhuǎn)發(fā)給了8765。
    改一下請求http://localhost:8766/api-ribbon/feignHi?name=yunsheng
    返回錯(cuò)誤。因?yàn)閍pi-ribbon會轉(zhuǎn)發(fā)給8764,沒有對應(yīng)的服務(wù)。

過濾器功能

除了請求路由以外,zuul另一個(gè)重要功能就是filter了。
代碼

    @Component
    public class MyFilter extends ZuulFilter {
        @Override
        public String filterType() {
            /* 過濾時(shí)機(jī)
            pre:路由之前
            routing:路由之時(shí)
            post: 路由之后
            error:發(fā)送錯(cuò)誤調(diào)用
            */
            return "pre";
        }
    
        /**
         * 過濾器的優(yōu)先級
         * 數(shù)字越大優(yōu)先級越低
         *
         * @return
         */
        @Override
        public int filterOrder() {
            return 0;
        }
    
        /**
         * 該過濾器開關(guān)
         *
         * @return
         */
        @Override
        public boolean shouldFilter() {
            return true;
        }
    
        /**
         * 過濾器方法
         *
         * @return
         */
        @Override
        public Object run() {
            RequestContext ctx = RequestContext.getCurrentContext();
            HttpServletRequest request = ctx.getRequest();
    
            String username = request.getParameter("name");// 獲取請求的參數(shù)
            if (null != username && username.equals("yunsheng")) {// 如果請求的參數(shù)不為空,且值為chhliu時(shí),則通過
                ctx.setSendZuulResponse(true);// 對該請求進(jìn)行路由
                ctx.setResponseStatusCode(200);
                ctx.set("isSuccess", true);// 設(shè)值,讓下一個(gè)Filter看到上一個(gè)Filter的狀態(tài)
                return null;
            } else {
                ctx.setSendZuulResponse(false);// 過濾該請求,不對其進(jìn)行路由
                ctx.setResponseStatusCode(401);// 返回錯(cuò)誤碼
                ctx.setResponseBody("{\"result\":\"name is not correct!\"}");// 返回錯(cuò)誤內(nèi)容
                ctx.set("isSuccess", false);
                return null;
            }
        }
    }

測試一下,http://localhost:8766/api-ribbon/ribbonHi?name=yunshen
返回{"result":"name is not correct!"}

配置中心config

spring cloud的配置項(xiàng)管理是以托管在git倉庫中的文件為存儲介質(zhì)。
配置中心分為兩部分:server端負(fù)責(zé)連接git倉庫,得到配置文件。client端負(fù)責(zé)對外提供api,從server端查詢具體配置信息。

server端

  1. 新建工程
    勾選eureka discovert、config server

  2. 代碼

     @SpringBootApplication
     @EnableConfigServer
     public class ConfigApplication {
     
         public static void main(String[] args) {
             SpringApplication.run(ConfigApplication.class, args);
         }
     }
    

還是注解開啟對應(yīng)的功能

  1. 配置
    配置application.properties

     #spring.application.name=config-server
     server.port=8767
     
     
     # 配置git倉庫地址
     spring.cloud.config.server.git.uri=https://github.com/jedyang/springCloud/
     # 倉庫路徑
     spring.cloud.config.server.git.searchPaths=configRepo
     # 倉庫的分支
     spring.cloud.config.label=master
     # 用戶名和密碼,例子的是公開倉庫,不需要
     #spring.cloud.config.server.git.username=your username
     #spring.cloud.config.server.git.password=your password
    
  2. 創(chuàng)建配置文件
    這是我創(chuàng)建的配置文件

8.png
  1. 測試
    http://localhost:8767/thekey/dev/master
    看到響應(yīng):
    {"name":"thekey","profiles":["dev"],"label":"master","version":"d6a477aa1da5862e9e42553d644e136efbea9296","state":null,"propertySources":[]}
    但是。。其實(shí)這只代表能訪問到倉庫的某個(gè)分之下,并不是真的配置內(nèi)容。
    比如訪問http://localhost:8767/a/a/
    依然能得到響應(yīng):
    {"name":"a","profiles":["a"],"label":null,"version":"d6a477aa1da5862e9e42553d644e136efbea9296","state":null,"propertySources":[]}
    從響應(yīng)中我們也可以看出,比如訪問http://localhost:8767/a/b/c時(shí),a代表應(yīng)用名(在client中配置,現(xiàn)在是在server工程,不要急),b是環(huán)境如dev、test,c代表git上的分支,現(xiàn)在只有master(所以要么不寫,寫錯(cuò)會報(bào)錯(cuò))。

還有其他訪問方式:

/{application}/{profile}[/{label}]
/{application}-{profile}.yml
/{label}/{application}-{profile}.yml
/{application}-{profile}.properties
/{label}/{application}-{profile}.properties、

client端

  1. 新建工程
    勾選web、config client

  2. 配置
    這里是配置bootstrap.properties。一定要注意啊。坑。

     spring.application.name=config-client
     spring.cloud.config.label=master
     spring.cloud.config.profile=dev
     spring.cloud.config.uri= http://localhost:8767/
     server.port=8768
    

    最終對應(yīng)的文件是spring.application.name-spring.cloud.config.profile.properties

  3. 代碼

     @SpringBootApplication
     @RestController
     public class ConfigClientApplication {
     
         public static void main(String[] args) {
             SpringApplication.run(ConfigClientApplication.class, args);
         }
     
         @Value("${thekey}")
         String theValue;
     
         @RequestMapping(value = "/getValue")
         public String getValue() {
             return theValue;
         }
     }
    

    做一個(gè)rest服務(wù)查詢下配置的值。
    原理就是通過@Value注解取值

  4. 測試
    訪問http://localhost:8768/getValue
    得到配置的值

集群化

到現(xiàn)在為止,我們的配置服務(wù)是單點(diǎn)的,在生產(chǎn)環(huán)境要求高可用的情況下,是存在風(fēng)險(xiǎn)的。現(xiàn)在我們將其集群化。
集群化的方法也很簡單,將服務(wù)注冊到eureka,通過eureka服務(wù)調(diào)用。

這里我們還是復(fù)用之前的注冊中心8761。

  1. 改造server端
    在配置文件中加上
    eureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka/
    指定服務(wù)注冊地址

    啟動類加上@EnableEurekaClient注解

  2. 改造client端
    添加依賴

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

    修改配置

     spring.application.name=config-client
     spring.cloud.config.label=master
     spring.cloud.config.profile=dev
     #spring.cloud.config.uri= http://localhost:8767/
     server.port=8768
     
     eureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka/
     spring.cloud.config.discovery.enabled=true
     spring.cloud.config.discovery.serviceId=config-server
    

    可以看到,現(xiàn)在修改為通過serviceId來獲取服務(wù),如果有多個(gè)服務(wù),可以實(shí)現(xiàn)集群化和負(fù)載均衡。

3.測試
tips:client端一定要在server啟動成功后再起,因?yàn)樾枰獜膕erver獲取配置,如果解析不到,會報(bào)錯(cuò)。

服務(wù)鏈路追蹤

在復(fù)雜業(yè)務(wù)系統(tǒng)中,排查問題是一件痛苦的事情,尤其是超時(shí)問題。你必須知道全鏈路的服務(wù)調(diào)用關(guān)系,以及服務(wù)花費(fèi)的時(shí)間。

針對服務(wù)化應(yīng)用全鏈路追蹤的問題,Google發(fā)表了Dapper論文,介紹了他們?nèi)绾芜M(jìn)行服務(wù)追蹤分析。其基本思路是在服務(wù)調(diào)用的請求和響應(yīng)中加入ID,標(biāo)明上下游請求的關(guān)系。利用這些信息,可以可視化地分析服務(wù)調(diào)用鏈路和服務(wù)間的依賴關(guān)系。

Zipkin是對Dapper論文的開源實(shí)現(xiàn),Spring Cloud Sleuth對Zipkin進(jìn)行了封裝,以便加入spring cloud全家桶。

Spring Cloud Sleuth(以下簡稱sleuth)借用了Dapper的術(shù)語。

  • span。簡單的理解就是一個(gè)最小的服務(wù)單元。例如發(fā)送一個(gè)RPC請求是一個(gè)span,發(fā)送一個(gè)響應(yīng)給RPC請求也是一個(gè)span。每個(gè)span用64bit的唯一id標(biāo)示。span上會包含其他信息,如描述、注解(理解成標(biāo)簽),觸發(fā)這個(gè)span的上一個(gè)span的id,最重要的時(shí)間信息。

  • trace。就是有一個(gè)個(gè)span組成的調(diào)用鏈路。

  • annotion。我們javaer習(xí)慣稱為注解,但是這里理解成標(biāo)簽比較合適。常用的核心標(biāo)簽:

    • cs,client sent
    • sr,server received
    • ss,server sent
    • cr,client received
9.png

可以理解,sr-cs=網(wǎng)絡(luò)傳輸時(shí)間。ss-sr=服務(wù)處理時(shí)間。cr-cs得到整個(gè)服務(wù)需要的時(shí)間。

開始代碼

zipkin server

  1. 新建一個(gè)工程zipkin-server
    我的依賴如下:

     <dependencies>
         <dependency>
             <groupId>org.springframework.cloud</groupId>
             <artifactId>spring-cloud-starter-zipkin</artifactId>
         </dependency>
         <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-web</artifactId>
         </dependency>
    
         <dependency>
             <groupId>io.zipkin.java</groupId>
             <artifactId>zipkin-server</artifactId>
         </dependency>
    
         <dependency>
             <groupId>io.zipkin.java</groupId>
             <artifactId>zipkin-autoconfigure-ui</artifactId>
             <scope>runtime</scope>
         </dependency>
         <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-test</artifactId>
             <scope>test</scope>
         </dependency>
     </dependencies>
    

里面的zipkin-server這個(gè)依賴,在idea創(chuàng)建的時(shí)候選不到,但是又是必須的。有知道的麻煩告訴我一下。

  1. 代碼
    啟動器加注解@EnableZipkinServer

  2. 配置
    加一下端口server.port=9411
    最好使用這個(gè)端口,應(yīng)該是有依賴關(guān)系。我換成其他的會報(bào)錯(cuò)。還不清楚具體怎么依賴的。

創(chuàng)建相互調(diào)用的服務(wù)

我創(chuàng)建了兩個(gè)工程。app1和app2。

  1. 依賴

     <dependencies>
         <dependency>
             <groupId>org.springframework.cloud</groupId>
             <artifactId>spring-cloud-starter-zipkin</artifactId>
         </dependency>
         <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-web</artifactId>
         </dependency>
    
         <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-test</artifactId>
             <scope>test</scope>
         </dependency>
     </dependencies>
    
  2. 代碼

    App1:

     @SpringBootApplication
     @RestController
     public class App1Application {
     
         public static void main(String[] args) {
             SpringApplication.run(App1Application.class, args);
         }
     
         @Autowired
         private RestTemplate restTemplate;
     
         @Bean
         public RestTemplate getRestTemplate() {
             return new RestTemplate();
         }
     
         @RequestMapping("/hi1")
         public String callHi1() {
             return restTemplate.getForObject("http://localhost:9002/hi2", String.class);
         }
     
         @RequestMapping("/hi3")
         public String callHi3() {
     
             return "i'm hi 33333";
     
         }
     }
    

    App2:

     @SpringBootApplication
     @RestController
     public class ZipkinApp2Application {
     
         public static void main(String[] args) {
             SpringApplication.run(ZipkinApp2Application.class, args);
         }
     
         @Autowired
         private RestTemplate restTemplate;
     
         @Bean
         public RestTemplate getRestTemplate(){
             return new RestTemplate();
         }
     
         @RequestMapping("/hi2")
         public String callHi2(){
             return restTemplate.getForObject("http://localhost:9001/hi3", String.class);
         }
     }
    
  3. 配置

    APP1:

     server.port=9001
     spring.zipkin.base-url=http://localhost:9411
     spring.application.name=service-app1
    

    APP2:

     server.port=9002
     spring.zipkin.base-url=http://localhost:9411
     spring.application.name=service-app2
    

就是hi1-->hi2-->hi3

依次啟動服務(wù)。

查看http://localhost:9411/zipkin/

10.png

看一條鏈路

11.png

可以看到時(shí)間和標(biāo)簽

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,835評論 6 534
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,676評論 3 419
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,730評論 0 380
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,118評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,873評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,266評論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,330評論 3 443
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,482評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,036評論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,846評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,025評論 1 371
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,575評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,279評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,684評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,953評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,751評論 3 394
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,016評論 2 375

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