在IDEA上創建SpringCloud常用組件

目錄

1,創建父工程

2,創建euraka-server

3,創建provider

4,創建consumer,Ribbon模式

5,創建consumer,Feign模式

6,在consumer-ribbon中使用斷路器Hystrix

7,在consumer-feign中使用斷路器Hystrix

8,創建路由網關 ZUUL

9,在路由網關中創建過濾器

10,創建配置中心,config-server

11,創建配置中心,config-client


1,創建父工程

菜單欄File->new->project,選擇Java就可以。

一路next,填好工程名字,選好地址,最後finish。

 

2,創建euraka-server

Euraka是SpringCloud的註冊中心,生產者和消費者都要在註冊中心註冊。

在工程上右鍵,新建module,選擇Srping Initializr。

右側Choose Initializr Service URL選擇Default就可以,next。

填好Project信息,我填的:

Group:com.lkforce.cloud

Artifact:eureka-server

Description:spring cloud學習

Package:eureka.server

其他的都默認。

next,左側選擇Spring Cloud Discovery,中間選擇Eureka Server。

next,填個module名字,finish。

然後就是漫長的創建module和下載jar包的過程,然後我們得到了這樣一個module:

改一下配置文件,在application.property裏:

server.port: 8761

spring.application.name: eurka-server

eureka.instance.hostname: localhost
eureka.client.registerWithEureka: false
eureka.client.fetchRegistry: false
eureka.client.service-url.defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/

在啓動類前面加上註解@EnableEurekaServer,否則服務啓動後無法訪問Euraka:

加好註解,啓動服務,在地址欄輸入http://localhost:8761/,可以看到Eureka服務的界面:

其中紅框的部分就是已經註冊在Eureka的服務,現在還沒有。雖然Eureka本身也是一個服務,但因爲之前的配置:

eureka.client.registerWithEureka: false

使得Eureka自己不會把自己註冊上去。

 

3,創建provider

provider是服務的提供者。

創建一個新的module,還是選擇Spring Initializr,Group和前面的eureka-server寫成一樣的com.lkforce.cloud,Artifact和Name就叫provider-server吧:

next,左側還是選擇Spring Cloud Discovery,中間選擇Eureka Discovery Client,另外,爲了提供對外服務,我們還要加上Web中的Spring Web選項:

next直到完成,我們得到了這樣的module:

然後依然是改application.properties配置文件:

server.port: 8762

spring.application.name: provider-a

eureka.client.service-url.defaultZone: http://localhost:8761/eureka/

還要記得在啓動類前面加註解,這次是@EnableEurekaClient,表示這個其實是一個Eureka的客戶端。

這個時候,此客戶端中不提供服務,也不會請求其他服務,啓動起來之後會立刻關閉。

 

下面創建一個Spring Cloud的Rest接口,然後啓動類就成了這樣:

package provider.server;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
@EnableEurekaClient
@RestController
public class ProviderServerApplication {

    public static void main(String[] args) {
        SpringApplication.run(ProviderServerApplication.class, args);
    }

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

    @RequestMapping("/hi")
    public String home(@RequestParam(value = "name", defaultValue = "this is default value") String name) {
        return "hi " + name + " ,i am from port:" + port;
    }
}

注意,要讓Rest接口生效,需要添加註解@RestController

啓動服務,再次訪問http://localhost:8761/

出現了剛剛啓動的provider服務,Application的名字是配置文件中的:

spring.application.name: provider-a

不過給轉成了大寫,看起來這個配置大小寫不敏感。

 

4,創建consumer,Ribbon模式

consumer是服務的消費者。

consumer調用provider有兩種模式,Ribbon模式和Feign模式,先創建一個Ribbon模式的consumer。

依然是創建module,還是選擇Spring Initializr,Group和前面的eureka-server寫成一樣的com.lkforce.cloud,Artifact和Name就叫consumer-ribbon吧:

next,和provider一樣選擇Spring Cloud Discovery->Eureka Discovery Client,還有Web->SpringWeb,然後我們得到了這樣的module:

照例修改配置文件:

server.port: 8764

spring.application.name: consumer-ribbon

eureka.client.service-url.defaultZone: http://localhost:8761/eureka/

照例在啓動類前添加註解:@EnableEurekaClient

 

下面創建一個對外接口,並在接口中調用上面provider的接口。

首先我們要有一個RestTemplate的對象,如果寫在啓動類裏,我們的啓動類就會變成這樣:

package consumer.ribbon;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

@SpringBootApplication
@EnableEurekaClient
public class ConsumerRibbonApplication {

    public static void main(String[] args) {
        SpringApplication.run(ConsumerRibbonApplication.class, args);
    }

    @Bean
    @LoadBalanced
    RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

其中@LoadBalanced註解代表開啓負載均衡。

 

創建Service,用來調用provider接口:

package consumer.ribbon;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

@Service
public class RibbonService {

    @Autowired
    RestTemplate restTemplate;

    public String hiService(String name) {
        return restTemplate.getForObject("http://provider-a/hi?name="+name,String.class);
    }
}

注意,這裏http後面直接寫Application的名字,不是項目名字,別寫錯了。

 

創建一個controller,使用這個RibbonService:

package consumer.ribbon;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HiController {

    @Autowired
    RibbonService ribbonService;

    @RequestMapping(value = "/hi")
    public String hi(@RequestParam String name) {
        return ribbonService.hiService(name);
    }
}

啓動服務,在地址欄輸入:http://localhost:8764/hi?name=test,可以看到結果:

 

5,創建consumer,Feign模式

Feign模式包含Ribbon和Hystrix。

照例創建module,還是選擇Spring Initializr,Group和前面的eureka-server寫成一樣的com.lkforce.cloud,Artifact和Name就叫consumer-feign:

next,除了選擇Spring Cloud Discovery->Eureka Discovery Client,還有Web->SpringWeb之外,還需要Spring Cloud Routing下的OpenFeign:

然後我們得到了這樣的module:

修改application.property:

server.port: 8765

spring.application.name: consumer-feign

eureka.client.service-url.defaultZone: http://localhost:8761/eureka/

在啓動類上加上註解:@EnableEurekaClient@EnableFeignClients

 

下面寫一個Service,用來調用provider:

package consumer.feign;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;

@FeignClient(value = "provider-a")
public interface FeignService {

    @GetMapping("/hi")
    String sayHiFromClientOne(@RequestParam(value = "name") String name);

}

再寫一個controller,提供對外接口並調用上面的service:

package consumer.feign;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HiController {

    @Autowired
    FeignService feignService;

    @GetMapping(value = "/hi")
    public String sayHi(@RequestParam String name) {
        return feignService.hiService( name );
    }

}

不知道爲什麼IDEA提示我feignService無法被Autowired,但是實際上並不影響編譯和運行。

啓動服務,在地址欄輸入http://localhost:8765/hi?name=test,可以看到結果:

 

6,在consumer-ribbon中使用斷路器Hystrix

斷路器Hystrix,作用是在provider被調用失敗多次後,斷路器打開,consumer再次調用該provider時,執行指定的斷路處理方法,可以防止雪崩。

在Ribbon模式使用斷路器時,首先向consumer-ribbon的pom文件中加入:

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

第二步,啓動類添加@EnableHystrix註解。

第三步,給RibbonService的方法前添加@HystrixCommand註解,用來設置斷路方法名,並寫好斷路方法,於是RibbonServiceService變成了這樣:

package consumer.ribbon;

import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

@Service
public class RibbonService {

    @Autowired
    RestTemplate restTemplate;

    @HystrixCommand(fallbackMethod = "hiServiceError")
    public String hiService(String name) {
        return restTemplate.getForObject("http://provider-a/hi?name=" + name, String.class);
    }

    public String hiServiceError(String name) {
        return "hi," + name + ",斷路機制啓動,hiServiceError";
    }
}

創建的註解@HystrixCommand(fallbackMethod = "hiServiceError")代表斷路後要執行的方法是hiServiceError方法。

下面啓動服務,啓動euraka-server,provider-server,consumer-ribbon,在地址欄輸入:

http://localhost:8764/hi?name=test

顯示的是:

hi test ,i am from port:8762

然後關閉provider-server,再次在地址欄輸入:

http://localhost:8764/hi?name=test

顯示的是:

hi,test,斷路機制啓動,hiServiceError

 

7,在consumer-feign中使用斷路器Hystrix

feign模式使用斷路器,不用引入其他的jar包。

首先,要在consumer-feign的配置文件中加入:

feign.hystrix.enabled: true

打開斷路器。

第二,和consumer-ribbon中配置斷路的方式不同,consumer-feign中把斷路處理配置在Service類上,在此之前,我們要有一個斷路處理類:

package consumer.feign;

import org.springframework.stereotype.Component;

@Component
public class FeignServiceHystric implements FeignService {
    @Override
    public String hiService(String name) {
        return "hi," + name + ",斷路機制啓動,hiServiceError";
    }
}

這個斷路處理類實現了原Service接口,所以要實現原Service中的所有方法,而這些實現的方法就會成爲原Service的斷路處理方法。

@Component註解別忘了加。

 

第三,有了斷路處理類,下面在原Service上添加配置,於是原Service變成了這樣:

package consumer.feign;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;

@FeignClient(value = "provider-a",fallback = FeignServiceHystric.class)
public interface FeignService {

    @GetMapping("/hi")
    String hiService(@RequestParam(value = "name") String name);

}

在@FeignClient註解裏添加的fallback = FeignServiceHystric.class參數,就是指定斷路處理類的參數。

下面啓動服務,啓動euraka-server,provider-server,consumer-feign,在地址欄輸入:

http://localhost:8765/hi?name=test

顯示的是:

hi test ,i am from port:8762

然後關閉provider-server,再次在地址欄輸入:

http://localhost:8765/hi?name=test

顯示的是:

hi,test,斷路機制啓動,hiServiceError

 

另外,如果配置文件中沒有加入feign.hystrix.enabled: true,那麼斷路器的配置不會生效,請求provider時不會執行斷路器處理類的方法,而是會拋出異常:

java.net.SocketTimeoutException: connect timed out

頁面呈現的效果則會是這樣:

 

8,創建路由網關 ZUUL

照例創建module,還是選擇Spring Initializr,Group和前面的eureka-server寫成一樣的com.lkforce.cloud,Artifact和Name就叫zuul-server:

next,除了選擇Spring Cloud Discovery->Eureka Discovery Client,還有Web->SpringWeb之外,還需要Spring Cloud Routing下的Zuul:

創建module後,修改application.property:

server.port: 8769

spring.application.name: service-zuul

eureka.client.serviceUrl.defaultZone: http://localhost:8761/eureka/

zuul.routes.api-a.path: /api-a/**
zuul.routes.api-a.service-id: consumer-ribbon
zuul.routes.api-b.path: /api-b/**
zuul.routes.api-b.service-id: consumer-feign

其中zuul.routes就是路由部分

 

在啓動類添加@EnableEurekaClient@EnableZuulProxy註解

 

啓動服務,在地址欄輸入:

http://localhost:8769/api-a/hi?name=test

請求就會轉到consumer-ribbon服務的/hi?name=test接口,界面展示:

地址欄輸入:

http://localhost:8769/api-b/hi?name=test

請求就會轉到consumer-feign服務的/hi?name=test接口,界面展示:

 

9,在路由網關中創建過濾器

用到的依然是Zuul組件,我們需要定義一個過濾器類,繼承ZuulFilter類,比如:

package zuul.server;

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

@Component
public class ThisIsAFilter extends ZuulFilter {

    /**
     * pre 路由之前攔截
     * routing 路由時攔截
     * post 路由後攔截
     * error 發送錯誤調用
     */
    @Override
    public String filterType() {
        return "pre";
    }

    @Override
    public int filterOrder() {
        return 0;
    }

    /**
     * 是否需要過濾
     *
     * @return true則表示需要過濾
     */
    @Override
    public boolean shouldFilter() {
        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest request = ctx.getRequest();
        String key = request.getParameter("key");
        if (key != null) {
            return false;
        }
        return true;
    }

    @Override
    public Object run() throws ZuulException {
        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest request = ctx.getRequest();
        String name = request.getParameter("name");
        if (name.length() > 5) {
            ctx.setSendZuulResponse(false);
            ctx.setResponseStatusCode(401);
            try {
                ctx.getResponse().getWriter().write("error:name.length>5");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }
}

以上是一個自定義的過濾器類,繼承ZuulFilter類。

繼承ZuulFilter類後,需要重寫以下方法:

filterType(),返回過濾的節點。返回pre字符串表示在路由之前過濾。

filterOrder(),表示過濾順序。

shouldFilter(),是否需要過濾,返回true則表示需要過濾。上面的代碼中,請求沒有key參數則需要過濾。

run(),過濾的方法,具體的過濾邏輯。在上面的代碼中,name參數的長度大於5則請求失敗,不會分發給服務。

 

啓動服務,在地址欄輸入:

http://localhost:8769/api-a/hi?name=test

則頁面展示:

hi test ,i am from port:8762

 

地址欄輸入:

http://localhost:8769/api-a/hi?name=testaaa

則頁面展示:

error:name.length>5

 

地址欄輸入:

http://localhost:8769/api-a/hi?name=testaaa&key=1

則頁面展示:

hi test ,i am from port:8762

 

10,創建配置中心,config-server

在創建配置中心的server之前,我們需要有一個可訪問的配置文件倉庫,可以放在git,svn,或者本地。我在git上創建了一個工程,裏面有兩個文件:

項目路徑:

https://github.com/liankgun/spring-cloud-config-repository

 

文件:

spring-cloud-config-repository/config-dev.properties

內容:

hello:this is config-dev.properties

 

文件:

spring-cloud-config-repository/config-test.properties

內容:

hello:this is config-test.properties

 

下面創建module,還是選擇Spring Initializr,Group和前面的eureka-server寫成一樣的com.lkforce.cloud,Artifact和Name就叫config-server:

next,選擇Spring Cloud Config->Config Server:

然後我們得到了這樣的module:

修改配置文件:

server.port: 8770

spring.application.name: config-server
spring.cloud.config.server.git.uri: https://github.com/liankgun/spring-cloud-config-repository

給啓動類添加註解:@EnableConfigServer

 

啓動服務,在瀏覽器地址欄輸入:

http://localhost:8770/config-dev.properties

返回結果:

也就是在github中添加的config-dev.properties文件的內容。

 

地址欄輸入:

http://localhost:8770/config-test.properties

返回結果:

也就是在github中添加的config-test.properties文件的內容。

 

地址欄輸入:

http://localhost:8770/config-client/dev.properties

返回結果:

圖32

也就是在github中添加的config-dev.properties文件的文件信息。

說明config-server可用

 

11,創建配置中心,config-client

配置中心可以統一管理服務的外部參數,方便的進行統一配置和統一修改。

新建module,還是選擇Spring Initializr,Group和前面的eureka-server寫成一樣的com.lkforce.cloud,Artifact和Name就叫config-client:

next,選擇Spring Cloud Config->Config Client和Web->SpringWeb:

然後我們得到了這樣的module:

修改配置文件名,默認的配置文件名是application.properties,需要修改爲bootstrap.properties,或者bootstrap.yml,據說config-server端口是8888時不用改名。

另外修改配置文件內容:

server.port: 8771

spring.application.name: config-client

spring.cloud.config.name: config-client
spring.cloud.config.label: master
spring.cloud.config.uri: http://localhost:8770/
spring.profiles.active: dev

要注意,spring.cloud.config.name這一項的名字得和github上的配置文件名字對應,比如上面的配置:

spring.cloud.config.name: config-client

那麼在github上的配置文件的名字就得是config-client開頭,後面加下劃線再加環境名,比如:

  • config-client-dev.properties
  • config-client-test.properties
  • config-client.properties
  • config-client-dev.yml
  • config-client-test.yml
  • config-client.yml

沒有環境名的就代表默認配置。

這次啓動類不用加什麼註解。

 

創建一個controller,提供對外接口:

package config.client;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/config")
public class ConfigClientController {
    @Value("${hello}")
    private String hello;

    @GetMapping("/getConfigHello")
    public String getConfigHello() {
        return hello;
    }
}

啓動服務,在地址欄輸入:

http://localhost:8771/config/getConfigHello

返回結果:

也就是github上config-client-dev.properties這個配置文件中,hello參數的值。

之所以可以獲得hello參數,是因爲這個配置:

spring.cloud.config.uri: http://localhost:8770/

再加上:

spring.cloud.config.name: config-client

再加上:

spring.profiles.active: dev

所以config-client服務一啓動的時候,就加載了

http://localhost:8770/config-client/dev

這個配置文件的內容。

另外,以上的配置是沒有自動刷新功能的,也就是說,如果github上配置文件的內容變了,server端和client端不能立即響應,關於自動刷新的策略網上有很多。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章