SpringCloud入門(eureka集羣和feign負載均衡)

什麼是springcloud?

這是spring官網對springcloud的介紹,大致意思就是:讓分佈式系統簡單化。springcloud是建立在springboot之上的,也就是說他是需要依賴springboot的,因此學習springcloud首先就要了解springboot。在上一篇文章中有介紹到springboot。

這張圖說明了Spring Cloud是實施微服務的一系列套件,包括:服務註冊與發現、斷路器、服務狀態監控、配置管理、智能路由、一次性令牌、全局鎖、分佈式會話管理、集羣狀態管理等。

下面就用IDEA搭建springcloud項目,來看看springcloud是如何集成這些組件。

搭建一個小型的springcloud項目

1.首先使用IDEA創建一個maven父工程,在pom.xml裏引入需要的jar包依賴,dependencyManagement用來管理jar包版本。

<dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Dalston.SR1</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>1.5.9.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>5.0.4</version>
            </dependency>
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>druid</artifactId>
                <version>1.0.31</version>
            </dependency>

            <dependency>
                <groupId>org.mybatis.spring.boot</groupId>
                <artifactId>mybatis-spring-boot-starter</artifactId>
                <version>1.3.0</version>
            </dependency>
            <dependency>
                <groupId>ch.qos.logback</groupId>
                <artifactId>logback-core</artifactId>
                <version>1.2.3</version>
            </dependency>
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>${junit.version}</version>
                <scope>test</scope>
            </dependency>
            <dependency>
                <groupId>log4j</groupId>
                <artifactId>log4j</artifactId>
                <version>${log4j.version}</version>
            </dependency>
        </dependencies>
    </dependencyManagement>

2.然後創建子工程,首先創建一個provider工程,作爲服務提供者。

右鍵選中父工程後new一個module,選擇maven就可以新建一個子工程了。

每創建一個新工程首先在pom.xml裏添加一些基礎的依賴。

 <dependencies>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId><!--簡化javabean開發的jar,用註解的方式快速寫setter,getter,-->
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</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-web</artifactId>
        </dependency>

        <!-- actuator監控信息完善 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>Dalston.SR1</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId><!--eureka組件,後面沒帶server,即爲客戶端client-->
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-dependencies</artifactId>
            <version>1.5.9.RELEASE</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.0.4</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.0.31</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.3.0</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId><!--因該是將javabean變爲配置文件的jar包-->
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-core</artifactId>
            <version>1.2.3</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>${junit.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>${log4j.version}</version>
        </dependency>
    </dependencies>

接着是編寫application.yml文件

server:
  port: 8001
spring:
  application:
    name: userapi       #服務提供者名字,最終會顯示在eureka註冊中心的名字上,消費者通過這個名字找到服務並調用
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/testspringboot
    username: root
    password: root
    type: com.alibaba.druid.pool.DruidDataSource  #阿里巴巴的連接池
    dbcp2:
      max-total: 5                                 #最大維持連接數
      min-idle: 5                                   #最小維持連接數
      initial-size: 5                               #初始化大小
      max-wait-millis: 200                           #等待連接獲取的最大等待時間
  jpa:
    hibernate:
      ddl-auto: update                              #若數據庫中有表,則不創建。
    show-sql: true

這裏的持久層框架使用的是SpringJPA,比較方便。

下面看下provide工程的目錄結構:

 這裏provider8001代表是運行在8001端口上的服務提供者,後面要實現負載均衡還會建立多個服務提供者。

注意每個子工程都是一個獨立的springboot項目,因此每次新建完一個子module都要編寫springboot的啓動類。且啓動類的位置要包含其他所有的包,這樣這個啓動類在能在啓動時加載其他的需要放入spring容器中的bean,也就是對其他的包進行管理。

啓動類的通用寫法:

package com.tellhow;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

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

controller層代碼:

package com.tellhow.controller;

import com.tellhow.repository.UserRepository;
import com.tellhow.entity.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
@RequestMapping("user")
public class UserController {

    @Autowired
    private UserRepository userRepository;

    @RequestMapping("test")
    public String testProvider(){
        return "Hello world";
    }

    @RequestMapping(value = "/users",method = RequestMethod.GET)
    public List<User> getUsers(){
        return userRepository.findAll();
    }

    @RequestMapping(value = "/user",method = RequestMethod.POST)
    public boolean addUser(User user){
        userRepository.save(user);
        return true;
    }

    @RequestMapping(value = "/user",method = RequestMethod.PUT)
    public boolean updateUser(User user){
        userRepository.save(user);//如果主鍵存在則修改。
        return true;
    }


}

主要就是查詢數據庫裏的user列表,用來展示分佈式調用的。持久層實現參見上一篇文章springboot整合jpa.

服務層就大致寫完了,寫完之後需要驗證一下是否正常可用,保證下寫消費者調用時能正常運行。

訪問http://localhost:8001/user/users 運行結果:

代表服務消費者可以正常啓動,下面就可以來寫消費者了。

1.與建立服務提供者同樣的操作,右鍵父工程,新建module,添加pom.xml依賴,編寫applicaton.yml文件。編寫啓動類

applicaton.yml文件裏只需要配置一個端口即可

server:
  port: 80

啓動類:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.context.annotation.ComponentScan;


@SpringBootApplication
@ComponentScan(basePackages = {"com.tellhow"})//指定啓動時掃描哪些需要管理的包,若將啓動類放在需要管理的包的上一級,則不需要此註解
public class ConsumerApplicationRun {
    public static void main(String args[]){
        SpringApplication.run(ConsumerApplicationRun.class,args);
    }
}

conctroller層:通過RestTemplate調用服務提供者。由此可以看出,springcloud的分佈式調用,是用rest來進行調用,不同於dubbo的RPC調用。

使用RestTemplate之前需要將其注入到spring容器中。

package com.tellhow.cfgBean;

import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RandomRule;
import com.netflix.loadbalancer.RetryRule;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

@Configuration
public class ConfigBean {
    @Bean
    public RestTemplate getRestTemplate(){
        return new RestTemplate();
    }
}

controller:

package com.tellhow.controller;

import com.tellhow.beans.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import java.util.List;

@RestController
public class UserController {
    @Autowired
    public RestTemplate restTemplate;

    public static final String PREX="http://localhost:8001";
    //public static final String PREX="http://USERAPI";//通過eureka上註冊的服務名來調用

    @RequestMapping(value = "/consumer/user",method = RequestMethod.GET)
    public List<User> getUsers(){
        return restTemplate.getForObject(PREX+"/user/users",List.class);
    }
}

然後先啓動服務提供者,再啓動服務消者,查看結果,因爲消費者工程的端口後是80,因此可以訪問地址可以省略端口

訪問:http://localhost/consumer/user

可以看到,成功調用。

加入eureka註冊中心

eureka是用來做服務註冊與發現的,也就是服務提供者將服務註冊到eureka註冊中心,然後消費者在註冊中心對服務進行調用消費。例如:想要開淘寶店需要先到淘寶平臺註冊店鋪,然後買家纔可以在淘寶上找到該店鋪進行消費購買。

當然eureka還有很多功能:通過心跳檢測、健康檢查、客戶端緩存等機制,保證了系統具有高可用和靈活性。

接下來建立一個eureka的子工程。建立module的過程不做贅述了。

pom.xml依賴

<dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka-server</artifactId><!--eureka 服務-->
        </dependency>
    </dependencies>

application.yml

server:
  port: 7000
eureka:
  instance:
    hostname: eureka7000.com  #eureka主機名
  client:
    register-with-eureka: false   #表示不向註冊中心註冊自己
    fetch-registry: false          # 表示自己端就是註冊中心,職責就是維護服務實例,不需要檢索服務。
    service-url:
      defaultZone: http://eureka7001.com:7000/eureka/

啓動類:

package com.tellhow;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

@SpringBootApplication
@EnableEurekaServer         //Eureka服務類,接受其他服務註冊進來。
public class EurekaApplicationRun {
    public static void main(String args[]){
        SpringApplication.run(EurekaApplicationRun.class,args);
    }
}

如此,eureka註冊中心就建立完成了,之後需要在privoder工程裏添加向註冊中心註冊的相關東西。pom.xml添加eureka依賴

首先provider工程啓動類上添加註解:@EnableEurekaClient //啓動eureka客戶端

然後application.yml改爲如下:

server:
  port: 8001
spring:
  application:
    name: userapi       #服務提供者名字,最終會顯示在eureka註冊中心的名字上,消費者通過這個名字找到服務並調用
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/testspringboot
    username: root
    password: root
    type: com.alibaba.druid.pool.DruidDataSource  #阿里巴巴的連接池
    dbcp2:
      max-total: 5                                 #最大維持連接數
      min-idle: 5                                   #最小維持連接數
      initial-size: 5                               #初始化大小
      max-wait-millis: 200                           #等待連接獲取的最大等待時間
  jpa:
    hibernate:
      ddl-auto: update                              #若數據庫中有表,則不創建。
    show-sql: true
eureka:
  client:
    service-url:
      defaultZone: http://eureka7000.com:7000/eureka  #後面必須加個eureka 才能註冊上去。

  instance:
    instance-id: userAPI-8001 #註冊在eureka上的服務的實例的id
    prefer-ip-address: true    #顯示IP地址,方便查找
info:
  app.name: TestMaven
  company.name: www.tellhow.com
  .artifactId: $project.artifactId$
  buibuildld.version: $project.version$

消費者工程裏也做相似的改動:啓動類添加註解:

@EnableEurekaClient //eureka是服務註冊中心,所以對用向服務中心註冊服務的,和向服務中心消費服務的,都是eureka客戶端

application.yml

server:
  port: 80
eureka:
  client:
    register-with-eureka: false #消費者 不向服務端註冊
    service-url:
      defaultZone: http://eureka7000.com:7000/eureka/

修改完成後先啓動註冊中心工程,再啓動服務提供者工程,接着是消費者。

啓動完成後訪問:http://127.0.0.1:7000如果能出現一下畫面則代表eureak註冊中心工程建立成功。

 eureka服務中心能正常啓動後,將consumer項目controller裏的訪問連接前綴由http://localhost:8001換爲:http://USERAPI,也就是通過服務端註冊在服務中心的id來找到要調用的服務。

        用到服務中心之後,有一個問題,就是當服務中心掛掉之後,那麼消費者便無法找到要調用的服務了,系統就掛了。爲了解決此問題,就可以用到eureka的集羣,也就是高可用。

eureka集羣非常簡單,只需要按照之前建立eureka項目的步驟再多建立幾個eureka註冊中心就ok了,我這裏再建立個,並演示集羣eureka的application.yml文件該怎麼寫

#第一個eureka的yml文件:
server:
  port: 7000
eureka:
  instance:
    hostname: eureka7000.com  #eureka主機名
  client:
    register-with-eureka: false   #表示不向註冊中心註冊自己
    fetch-registry: false          # 表示自己端就是註冊中心,職責就是維護服務實例,不需要檢索服務。
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/


#第二個eureka的yml文件:
server:
  port: 7001
eureka:
  instance:
    hostname: eureka7001.com
  client:
    fetch-registry: false
    register-with-eureka: false
    service-url:
      defaultZone: http://eureka7000.com:7000/eureka/,http://eureka7002.com:7002/eureka/

#第三個eureka的yml文件:
server:
  port: 7002
eureka:
  instance:
    hostname: eureka7002.com
  client:
    fetch-registry: false
    register-with-eureka: false
    service-url:
      defaultZone: http://eureka7000.com/eureka/,http://eureka7001.com/eureka/


然後將消費端和生產者的yml文件裏的defaultZone都加上上面的訪問地址,便可以將服務註冊進三個註冊中心了,這樣某一個註冊中心掛掉的話對整個系統沒有任何影響。

現在註冊中心已經高可用了,但又有一個新的問題,如果服務提供者掛了,那麼系統也就掛了。所以也需要實現服務提供者的高可用,實現步驟與實現eureka高可用的步驟一樣,建立多個服務提供者,註冊進三個註冊中心即可。步驟就省略了。

那麼有了三個服務提供者,每次消費者在eureka上調用服務時,由哪個服務提供者來提供服務呢,怎樣才能做到高效呢。也就是怎樣做到負載均衡來提高效率呢。

springcloud提供了兩種負載均衡的組件:Ribbon和Feign。

一般我們都使用Feign來做負載均衡,因爲Feign本身就包含了Ribbon,而且Feign是使用面向接口編程的方式,更符合大衆的方式。

使用一個新組件 第一步pom.xml添加依賴,第二步修改application.yml文件 第三部相關編碼。

添加依賴:

<!-- feign相關 -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-feign</artifactId>
</dependency>

yml文件不用需改,

在消費者啓動類里加入Feign客戶端註解。

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

編寫service接口

package com.tellhow.service;

import com.tellhow.beans.User;
import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import java.util.List;

@FeignClient(value = "USERAPI")
public interface UserService {

    /**
     * 此接口還是相當於rest請求轉發,會去找eureak裏USERAPI這個服務裏,根據RequestMapping的路徑找對應的方法,其實就是找provider裏的方法。
     * @return
     */
    @RequestMapping(value = "user/users",method = RequestMethod.GET)
    public List<User> getUsers();
}

controller層:

package com.tellhow.controller;

import com.tellhow.beans.User;
import com.tellhow.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
public class UserController {

    @Autowired
    private UserService userService;

    @RequestMapping("/feign/users")
    public List<User> getUsers(){
        return userService.getUsers();
    }

}

然後依次啓動各個項目,訪問:

http://localhost/feign/users

就能看到結果啦。

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