springcloud2.X核心組件搭建教程(eureka、config、stream、feign、hystrix、zuul)

1.基於springboot2.0.5,springcloud2.X(Finchley.SR1),本文會先搭建一個簡單的springcloud示例,包括feign、hystrix、zuul、springcloud-config、springcloud-stream這些的整合。

2.項目模塊說明

eureka-server 註冊中心

config-server 配置中心

user-service 用戶服務

order-service 訂單服務

message-service 消息服務

gateway 路由服務

consumer 前端服務

2.開始

2.1 註冊中心

依賴如下:

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

application.yml

server:
  port: 8761

eureka:
  instance:
    hostname: localhost
  client:
    registerWithEureka: false #是否將自身註冊到eureka
    fetchRegistry: false #是否抓取註冊信息
    serviceUrl:
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/

啓動類加上註解@EnableEurekaServer

2.2 服務提供者

order-service,message-service,user-service,三者依賴都一樣:

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

order-service的配置文件:
spring:
  application:
    name: order-service

server:
  port: 8763

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

user-service的配置文件:

spring:
  application:
    name: user-service

server:
  port: 8762

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

message-service的配置文件:

spring:
  application:
    name: message-service

server:
  port: 8764

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

然後我們分別啓動這4個項目,瀏覽器輸入http://localhost:8761/ 可以看到服務啓動成功。

2.3 整合feign和hystrix

2.3.1 新建模塊consumer,前端模塊,consumer不需要註冊到eureka,在這裏我們通過consumer去調用服務集羣。

2.3.2 consumer引入feign依賴

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
        </dependency>

2.3.3 給order-service新建一個簡單的controller

@RestController
public class OrderController {

    static Logger log = LogManager.getLogger(OrderController.class);

    @RequestMapping(value = "/get")
    public OrderEntity get(LocalDateTime dateTime){
        OrderEntity orderEntity = new OrderEntity();
        orderEntity.setId(8L);
        orderEntity.setCustomerName("test測試");
        orderEntity.setDate(dateTime);
        return orderEntity;
    }

    @RequestMapping(value = "/getById")
    public OrderEntity get(Long id){
        OrderEntity orderEntity = new OrderEntity();
        orderEntity.setId(id);
        orderEntity.setCustomerName("test測試");
        orderEntity.setDate(LocalDateTime.now());
        return orderEntity;
    }

    @RequestMapping(value = "/normal")
    public String normal(){

        return "1";
    }

    @RequestMapping(value = "/abnormal")
    public String abNormal() throws InterruptedException {
        log.info("abnormal-start:{}",LocalDateTime.now());
        Thread.sleep(8000);
        log.info("abnormal-end:{}",LocalDateTime.now());
        return "2";
    }

    @RequestMapping(value = "/abnormalWithCallBack")
    public String abnormalWithCallBack() throws InterruptedException {
        log.info("abnormalWithCallBack-start:{}",LocalDateTime.now());
        Thread.sleep(6000);
        log.info("abnormalWithCallBack-end:{}",LocalDateTime.now());
        return "3";
    }

}

2.3.4 consumer模塊中新建feign客戶端

@FeignClient(name = "order",url = "http://localhost:8861/order",fallbackFactory = OrderClientFallBack.class)
public interface OrderClient {

    @RequestMapping(method = RequestMethod.GET,value = "/get")
    OrderEntity getOne(LocalDateTime dateTime);

    @RequestMapping(method = RequestMethod.GET,value = "/getById")
    OrderEntity getOne(Long id);

    @RequestMapping(method = RequestMethod.GET,value = "/normal")
    String normal();

    @RequestMapping(method = RequestMethod.GET,value = "/abnormal")
    String abnormal();

    @RequestMapping(method = RequestMethod.GET,value = "/abnormalWithCallBack")
    String abnormalWithCallBack();
}

fallback處理:

@Component
public class OrderClientFallBack implements FallbackFactory<OrderClient> {

    static Logger log = LogManager.getLogger(OrderClientFallBack.class);

    @Override
    public OrderClient create(Throwable throwable) {
        return new OrderClient() {

            @Override
            public OrderEntity getOne(LocalDateTime dateTime) {
                return null;
            }

            @Override
            public OrderEntity getOne(Long id) {
                return null;
            }

            @Override
            public String normal() {
                return null;
            }

            @Override
            public String abnormal() {
                return null;
            }

            @Override
            public String abnormalWithCallBack() {
                log.info("abnormalWithCallBack - fail");
                return "abnormalWithCallBack - fail";
            }
        };

    }
}

consumer配置文件如下:增加超時時間的設置(feign默認1秒鐘超時),並打開feign-hystrix(默認是關),一般可以將hystrix的超時時間設置的比feign的超時時間長一些,否則feign的重試(如果配置了)將會失效。

spring:
  application:
    name: consumer
server:
  port: 8862

feign:
  client:
    config:
      default:
        connectTimeout: 7000
        readTimeout: 7000
  hystrix:
    enabled: true

hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 8000

2.3.5 consumer增加controller

@RestController
@RequestMapping(value = "/consumer/test")
public class ConsumerController {

    static Logger log = LogManager.getLogger(ConsumerController.class);

    @Autowired
    OrderClient orderClient;

    @RequestMapping(value = "/order/get" )
    public OrderEntity get(){
        return orderClient.getOne(LocalDateTime.now());
    }

    @RequestMapping(value = "/order/getById" )
    public OrderEntity getById(){
        return orderClient.getOne(8L);
    }

    @RequestMapping(value = "/order/normal" )
    public String normal(){
        return orderClient.normal();
    }

    @RequestMapping(value = "/order/abnormal" )
    public String abnormal(){
        return orderClient.abnormal();
    }

    @RequestMapping(value = "/order/abnormalWithCallBack" )
    public String abnormalWithCallBack(){
        try {
            String returnStr = orderClient.abnormalWithCallBack();
            log.info("s:{}",returnStr);
            return returnStr;
        }catch (Exception e){
            //FeignException
            log.error("xxx",e);
            return "catch Exception";
        }
    }
}

2.3.6 啓動類上加上@EnableFeignClients、@EnableHystrixDashboard、@EnableCircuitBreaker。

2.4 整合zuul

新建gateway項目,依賴如下:

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

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

配置文件如下:

spring:
  application:
    name: gateway

server:
  port: 8861

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

zuul:
  routes:
    order:
      path: /order/**
      serviceId: order-service
#      url: http://localhost:8763
    user:
      path: /user/**
      serviceId: user-service
  host:
    connect-timeout-millis: 10000
    socket-timeout-millis: 10000

order-service:
  ribbon:
    ReadTimeout: 10000
    ConnectTimeout: 10000

#超時配置說明:
#如果路由方式是serviceId的方式,配置爲:zuul:routes:order:serviceId,那麼ribbon的超時配置生效(order-service:ribbon:ReadTimeout)
#如果如果是url的方式,配置爲:zuul:routes:order:url,則zuul.host開頭的生效。

啓動類加上註解@EnableZuulProxy開啓zuul

到這裏有幾個超時時間要注意以下,一個是consumer的feign客戶端超時時間,一個是consumer啓用的hystrix的超時時間,還有一個是zuul的超時時間。只要其中一個超時了,就會觸發fallback。

2.5 整合spring cloud cofig

先在git上準備一下環境,如下:

其中test內容爲:

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://192.168.1.101:3306/test?characterEncoding=utf8&useSSL=false
    username: root
    password: root

version: test

test-string: abcdefghijk

新建config-server項目:

依賴如下:

        <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>

配置文件如下:

spring:
  application:
    name: config-server
  cloud:
    config:
      server:
        git:
          uri: https://github.com/pjypjy/learning-config #uri
          search-paths: /** #配置文件目錄
      label: master
      username: solider #github賬號
      password: 123456 #github密碼

server:
  port: 8862

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

啓動類加上註解@EnableConfigServer

爲order-service增加配置文件bootstrap.yml:

spring:
  cloud:
    config:
      discovery:
        enabled: true
        service-id: config-server
      profile: test

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

並且爲order-service增加依賴:

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <!--<version>5.1.47</version>-->
            <version>8.0.12</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>

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

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

order-service增加請求處理:

    @Autowired
    JdbcTemplate jdbcTemplate;

    @Autowired
    Environment environment;

    @RequestMapping(value = "/config/query")
    public List<Map<String, Object>> query() {
        String sql = "select * from user_info ";
        List<Map<String, Object>> list = jdbcTemplate.queryForList(sql);
        return list;
    }

    @RequestMapping(value = "/config/version")
    public String version() {
        String version = environment.getProperty("version");
        return version;
    }

    @RequestMapping(value = "/config/testString")
    public String testString() {
        String version = environment.getProperty("test-string");
        return version;
    }

    @RequestMapping(value = "/config/refresh")
    public String refresh() {
        RestTemplate restTemplate = new RestTemplate();
        HttpHeaders requestHeaders = new HttpHeaders();
        requestHeaders.setContentType(MediaType.APPLICATION_JSON);
        HttpEntity<String> requestEntity = new HttpEntity<>(null,requestHeaders);
        ResponseEntity<String> stringResponseEntity = restTemplate.postForEntity("http://localhost:8763/actuator/refresh", requestEntity, String.class);
        return stringResponseEntity.getStatusCode().value() + ":" +stringResponseEntity.getBody();
    }

order-service配置文件修改:這裏需暴露refresh

management:
  endpoints:
    web:
      exposure:
        include: ["refresh"]

然後先啓動config-server,再重新啓動order-service,測試一下,dataSource成功創建,可以查詢出數據,然後訪問/config/testString,得到字符串:abcdefghijk,然後我們將github上的test-string項改爲:abc,訪問/config/refresh,發送post請求讓order-service重新拉取配置,然後再訪問/config/testString可以得到:abc。

2.6 整合spring cloud stream

爲order-service、user-service、message-service增加依賴:

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

配置文件也統一增加rabbitmq的配置信息:

order-service的部分配置如下:

spring:
  application:
    name: order-service
  rabbitmq:
    host: 192.168.1.102
    port: 5672
    username: root
    password: root
    virtual-host: test
  cloud:
    stream:
      bindings:
        myTest:
          group: groupA

message-service的部分配置如下:

spring:
  application:
    name: message-service
  rabbitmq:
    host: 192.168.1.102
    port: 5672
    username: root
    password: root
    virtual-host: test

user-service部分配置如下:

spring:
  application:
    name: user-service
  rabbitmq:
    host: 192.168.1.102
    port: 5672
    username: root
    password: root
    virtual-host: test

首先是接收端:

爲這order-service、message-service項目增加接收端:

public interface ReceiveService {

    @Input("myTest")
    SubscribableChannel receive();

}
啓動類增加代碼:
    @StreamListener("myTest")
    public void myReceive1(byte[] msg){
        System.out.println("===receive:"+new String(msg));
    }

啓動類增加註解

@EnableBinding(value = {ReceiveService.class})

然後我們把user-service改造成發送端

爲user-service增加發送端:

public interface ReceiveService {

    @Input("myTest")
    SubscribableChannel receive();

}

爲user-service增加註解:

@EnableBinding(value = {SendService.class})

爲user-service增加controller:

@RestController
public class UserController {

    @Autowired
    SendService sendService;


    /**
     * 1.如果在配置文件中未配置消費者組,系統會自動生成一個臨時隊列,連接斷開,隊列消失,隊列名:myTest.anonymous.fsd15hu64....隨機生成
     * 2.如果配置了消費者組,則自動生成隊列爲: myTest.消費者組名稱 (非臨時隊列)
     * 3.默認發送消息,會向所有消費者組推送消息,如果一個有多個消費者 在 同一個消費者組裏,消息會輪詢發給這個組裏的消費者
     * @return
     */
    @RequestMapping(value = "/mySend")
    public boolean mySend(){
        Message msg = MessageBuilder.withPayload("from: user-service  to: order-service".getBytes()).build();
        boolean send = sendService.send().send(msg);
        return send;
    }

}

訪問/mySend可以看到message-service和order-service都能接收到消息,要注意,這裏沒有爲message-service配置消費者組,springcloud stream會自動創建臨時隊列,用於order-service接收消息。order-service設置了消費者組則自動生成隊列爲: myTest.消費者組名稱 (非臨時隊列)。消息會推送給所有消費者組,如果有多個項目都用了同一個消費者組(如groupA),消息將會輪詢發送給這些項目。
 

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