Spring Cloud簡介
Spring Cloud
是一個基於Spring Boot
實現的雲應用開發工具,它爲基於JVM的雲應用開發中涉及的配置管理、服務發現、斷路器、智能路由、微代理、控制總線、全局鎖、決策競選、分佈式會話和集羣狀態管理等操作提供了一種簡單的開發方式。
Spring Cloud
包含了多個子項目(針對分佈式系統中涉及的多個不同開源產品),比如:Spring Cloud Config
、Spring Cloud Netflix
、Spring Cloud CloudFoundry
、Spring Cloud AWS
、Spring Cloud Security
、Spring Cloud Commons
、Spring Cloud Zookeeper
、Spring Cloud CLI
等項目。
微服務架構
“微服務架構”在這幾年非常的火熱,以至於關於微服務架構相關的開源產品被反覆的提及(比如: netflix
、dubbo
),Spring Cloud
也因Spring
社區的強大知名度和影響力也被廣大架構師與開發者備受關注。
那麼什麼是“微服務架構”呢?簡單的說,微服務架構就是將一個完整的應用從數據存儲開始垂直拆分成多個不同的服務,每個服務都能獨立部署、獨立維護、獨立擴展,服務與服務間通過諸如RESTful API的方式互相調用。
對於“微服務架構”,大家在互聯網可以搜索到很多相關的介紹和研究文章來進行學習和了解。也可以閱讀始祖Martin Fowler
的《Microservices》(中文版翻譯點擊查看),本文不做更多的介紹和描述。
服務治理
在簡單介紹了Spring Cloud
和微服務架構之後,下面迴歸本文的主旨內容,如何使用Spring Cloud
來實現服務治理。
由於Spring Cloud
爲服務治理做了一層抽象接口,所以在Spring Cloud
應用中可以支持多種不同的服務治理框架,比如:Netflix Eureka
、Consul
、Zookeeper
。在Spring Cloud
服務治理抽象層的作用下,我們可以無縫地切換服務治理實現,並且不影響任何其他的服務註冊、服務發現、服務調用等邏輯。
所以,下面我們通過Eureka這種種服務治理的實現來體會Spring Cloud
這一層抽象所帶來的好處。
下一篇介紹基於Consul
的服務註冊與調用。
Spring Cloud Consul
首先,我們來嘗試使用Spring Cloud Consul
來實現服務治理。
Consul
是一套開源的分佈式服務發現和配置管理系統,由 HashiCorp
公司用 Go
語言開發。
它具有很多優點。包括: 基於 raft
協議,比較簡潔; 支持健康檢查, 同時支持 HTTP
和 DNS
協議 支持跨數據中心的 WAN
集羣 提供圖形界面 跨平臺,支持 Linux、Mac、Windows
下面,就來具體看看如何使用Spring Cloud Consul
實現服務治理,這裏使用Consul
爲服務註冊中心工程、consul-provider
服務提供者工程、consul-consumer
服務消費者工程。
準備工作
環境:
windows
jdk 8
maven 3.0
IDEA
構建工程
首先構建父工程,引入父工程依賴:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>cn.zhangbox</groupId>
<artifactId>spring-cloud-study</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<groupId>cn.zhangbox</groupId>
<artifactId>spring-cloud-consul</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<!--子模塊-->
<modules>
<module>consul-provider</module>
<module>consul-consumer</module>
</modules>
</project>
父工程取名爲:spring-cloud-consul
構建子工程,分別創建工程名爲:、consul-provider
、consul-consumer
並在pom中加入以下依賴:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>cn.zhangbox</groupId>
<artifactId>spring-cloud-consul</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<groupId>cn.zhangbox</groupId>
<artifactId>consul-provider</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>consul-provider</name>
<description>this project for Spring Boot</description>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<!-- 版本控制 -->
<commons-lang3.version>3.4</commons-lang3.version>
<commons-codec.version>1.10</commons-codec.version>
<mybatis-spring-boot.version>1.2.0</mybatis-spring-boot.version>
<lombok.version>1.16.14</lombok.version>
<fastjson.version>1.2.41</fastjson.version>
<druid.version>1.1.2</druid.version>
</properties>
<repositories>
<!-- 阿里私服 -->
<repository>
<id>aliyunmaven</id>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
</repository>
</repositories>
<dependencies>
<!-- mybatis核心包 start -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>${mybatis-spring-boot.version}</version>
</dependency>
<!-- mybatis核心包 end -->
<!-- 添加consul支持start -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- 添加consul支持start -->
<!-- SpringWEB核心包 start -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- SpringWEB核心包 end -->
<!-- Swagger核心包 start -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.6.1</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.6.1</version>
</dependency>
<!-- Swagger核心包 end -->
<!-- mysql驅動核心包 start -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!-- mysql驅動核心包 end -->
<!-- sprigTest核心包 start -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- sprigTest核心包 end -->
<!-- commons工具核心包 start -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>${commons-lang3.version}</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>${commons-codec.version}</version>
</dependency>
<!-- commons工具核心包 end -->
<!-- fastjson核心包 start -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fastjson.version}</version>
</dependency>
<!-- fastjson核心包 end -->
<!-- druid核心包 start -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>${druid.version}</version>
</dependency>
<!-- druid核心包 end -->
<!-- lombok核心包 start -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</dependency>
<!-- lombok核心包 end -->
</dependencies>
<!-- 添加cloud 依賴start -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Dalston.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<!-- 添加cloud 依賴end -->
<build>
<finalName>eureka-provider</finalName>
<plugins>
<!-- 添加maven構建插件start -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<!-- 添加maven構建插件end -->
</plugins>
</build>
</project>
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>cn.zhangbox</groupId>
<artifactId>spring-cloud-consul</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<groupId>cn.zhangbox</groupId>
<artifactId>consul-consumer</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>consul-consumer</name>
<description>this project for Spring Boot</description>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<!-- 版本控制 -->
<commons-lang3.version>3.4</commons-lang3.version>
<commons-codec.version>1.10</commons-codec.version>
<mybatis-spring-boot.version>1.2.0</mybatis-spring-boot.version>
<lombok.version>1.16.14</lombok.version>
<fastjson.version>1.2.41</fastjson.version>
<druid.version>1.1.2</druid.version>
</properties>
<repositories>
<!-- 阿里私服 -->
<repository>
<id>aliyunmaven</id>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
</repository>
</repositories>
<dependencies>
<!-- 添加consul支持start -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- 添加consul支持start -->
<!-- 添加feign支持start -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
<!-- 添加feign支持end -->
<!-- SpringWEB核心包 start -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- SpringWEB核心包 end -->
<!-- Swagger核心包 start -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.6.1</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.6.1</version>
</dependency>
<!-- Swagger核心包 end -->
<!-- sprigTest核心包 start -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- sprigTest核心包 end -->
<!-- commons工具核心包 start -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>${commons-lang3.version}</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>${commons-codec.version}</version>
</dependency>
<!-- commons工具核心包 end -->
<!-- fastjson核心包 start -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fastjson.version}</version>
</dependency>
<!-- fastjson核心包 end -->
<!-- lombok核心包 start -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</dependency>
<!-- lombok核心包 end -->
</dependencies>
<!-- 添加cloud 依賴start -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Dalston.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<!-- 添加cloud 依賴end -->
<build>
<finalName>eureka-provider</finalName>
<plugins>
<!-- 添加maven構建插件start -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<!-- 添加maven構建插件end -->
</plugins>
</build>
</project>
工程構建完成。
服務註冊中心搭建
Window下安裝:
window下安裝Consul
Linux下安裝:
CentOS 安裝 Consul 服務註冊和發現軟件
Consul安裝 linux環境
至此consul
註冊中心創建完成,window
下本機安裝的consul
瀏覽器輸入地址:http://localhost:8500linux
下安裝的只需要把localhost
換成對應的ip
即可
可以看到如下頁面,表示服務啓動OK
這裏這看到還沒有服務暴露出來,是因爲還沒創建服務提供者工程。
服務提供者工程配置
這裏服務提供者是使用之前SpringBoot進階教程 | 第三篇:整合Druid連接池以及Druid監控改造而來,這裏一樣的部分就不再重複說明,下面將說明新增的部分。
首先:修改application.yml
配置爲如下:
#公共配置
server:
port: 80
tomcat:
uri-encoding: UTF-8
spring:
application:
name: consul-provider
#激活哪一個環境的配置文件
profiles:
active: dev
#連接池配置
datasource:
driver-class-name: com.mysql.jdbc.Driver
# 使用druid數據源
type: com.alibaba.druid.pool.DruidDataSource
druid:
# 配置測試查詢語句
validationQuery: SELECT 1 FROM DUAL
# 初始化大小,最小,最大
initialSize: 10
minIdle: 10
maxActive: 200
# 配置一個連接在池中最小生存的時間,單位是毫秒
minEvictableIdleTimeMillis: 180000
testOnBorrow: false
testWhileIdle: true
removeAbandoned: true
removeAbandonedTimeout: 1800
logAbandoned: true
# 打開PSCache,並且指定每個連接上PSCache的大小
poolPreparedStatements: true
maxOpenPreparedStatements: 100
# 配置監控統計攔截的filters,去掉後監控界面sql無法統計,'wall'用於防火牆
filters: stat,wall,log4j
# 通過connectProperties屬性來打開mergeSql功能;慢SQL記錄
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
#配置consul獲取服務地址,這裏使用的是本地提供的註冊中心
cloud:
consul:
#consul服務地址
host: localhost
#consul端口號
port: 8500
discovery:
#配置註冊中心顯示當前服務的ip
prefer-ip-address: true
#配置當前服務的ip和端口號
#instance-id: ${spring.cloud.client.ipAddress}:${server.port}
#配置該項點擊ip會跳轉至對應的接口swagger頁面
health-check-url: http://${spring.cloud.client.ipAddress}:${server.port}/swagger-ui.html # ${server.port}爲該服務的端口號
#mybatis
mybatis:
# 實體類掃描
type-aliases-package: cn.zhangbox.springboot.entity
# 配置映射文件位置
mapper-locations: classpath:mapper/*.xml
# 開啓駝峯匹配
mapUnderscoreToCamelCase: true
---
#開發環境配置
server:
#端口
port: 8080
spring:
profiles: dev
# 數據源配置
datasource:
url: jdbc:mysql://101.132.66.175:3306/student?useUnicode=true&characterEncoding=utf8&useSSL=false&tinyInt1isBit=true
username: root
password: 123456
#日誌
logging:
config: classpath:log/logback.xml
path: log/consul-provider
---
#測試環境配置
server:
#端口
port: 80
spring:
profiles: test
# 數據源配置
datasource:
url: jdbc:mysql://127.0.0.1:3306/consul-provider?useUnicode=true&characterEncoding=utf8&useSSL=false&tinyInt1isBit=true
username: root
password: 123456
#日誌
logging:
config: classpath:log/logback.xml
path: /home/log/consul-provider
---
#生產環境配置
server:
#端口
port: 8080
spring:
profiles: prod
# 數據源配置
datasource:
url: jdbc:mysql://127.0.0.1:3306/consul-provider?useUnicode=true&characterEncoding=utf8&useSSL=false&tinyInt1isBit=true
username: root
password: 123456
#日誌
logging:
config: classpath:log/logback.xml
path: /home/log/consul-provider
其次:在config
目錄下增加SwaggerConfig
類加入以下代碼:
@Configuration
@EnableSwagger2
public class SwaggerConfig {
@Bean
public Docket userApi() {
Docket docket = new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage("cn.zhangbox.eureka.provider.controller"))//過濾的接口,有這掃描纔會看到接口信息
.paths(PathSelectors.any())
.build();
return docket;
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder().title("eureka服務端提供者接口平臺").description("服務相關數據接口")
.termsOfServiceUrl("http://www.zhang.box.cn/").contact("技術開發部")
.license("Licence Version 1.0").licenseUrl("#").version("1.0").build();
}
}
這裏如果不會使用swagger
的整合可以參考這篇文章:
SpringBoot非官方教程 | 第十一篇:SpringBoot集成swagger2,構建優雅的Restful API
接着:創建核心啓動類:
@EnableDiscoveryClient //使用該註解將註冊服務到consul
@SpringBootApplication
@MapperScan("cn.zhangbox.consul.provider.dao")//配置mybatis接口包掃描
public class ConsulProviderApplication extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(ConsulProviderApplication.class, args);
}
}
至此consul-provider
服務提供者創建完成,瀏覽器輸入地址:http://localhost:8500/
可以看到如下頁面,表示服務註冊OK
瀏覽器輸入地址:http://localhost:8080/swagger-ui.html後可以跳轉看到swagger
的頁面並可是測試服務提供者的接口,如下:
服務消費者工程配置
這裏構建的工程名爲consul-consumer
,上文已經構建了工程但是沒有創建目錄,這裏只需要在java目錄下創建config
、controller
,service
,包即可,這裏服務的消費是通過feign
來進行服務調用的,後面會有專門以篇文章來講feign
。
首先:在consul-consumer
工程下resource
目錄下添加application.yml
文件並加入以下配置:
#公共配置
server:
port: 80
tomcat:
uri-encoding: UTF-8
spring:
#服務名稱,更關鍵,使用feign進行服務消費將以此爲依據
application:
name: consul-consumer
#激活哪一個環境的配置文件
profiles:
active: dev
#配置consul獲取服務地址,這裏使用的是本地提供的註冊中心
cloud:
consul:
#consul服務地址
host: localhost
#consul端口號
port: 8500
discovery:
#配置註冊中心顯示當前服務的ip
prefer-ip-address: true
#配置當前服務的ip和端口號
#instance-id: ${spring.cloud.client.ipAddress}:${server.port}
#配置該項點擊ip會跳轉至對應的接口swagger頁面
health-check-url: http://${spring.cloud.client.ipAddress}:${server.port}/swagger-ui.html # ${server.port}爲該服務的端口號
---
#開發環境配置
server:
#端口
port: 8081
spring:
profiles: dev
#日誌
logging:
config: classpath:log/logback.xml
path: log/consul-consumer
---
#測試環境配置
server:
#端口
port: 8082
spring:
profiles: test
#日誌
logging:
config: classpath:log/logback.xml
path: /home/log/consul-consumer
---
#生產環境配置
server:
#端口
port: 8083
spring:
profiles: prod
#日誌
logging:
config: classpath:log/logback.xml
path: /home/log/consul-consumer
其次:在config
目錄下增加SwaggerConfig
類加入以下代碼:
@Configuration
@EnableSwagger2
public class SwaggerConfig {
@Bean
public Docket userApi() {
Docket docket = new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage("cn.zhangbox.eureka.consumer.controller"))//過濾的接口,有這掃描纔會看到接口信息
.paths(PathSelectors.any())
.build();
return docket;
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder().title("eureka服務端消費者接口平臺").description("服務相關數據接口")
.termsOfServiceUrl("http://www.zhang.box.cn/").contact("技術開發部")
.license("Licence Version 1.0").licenseUrl("#").version("1.0").build();
}
}
接着:在service
包下創建StudentConsumerService
類並加入以下代碼:
@FeignClient(value = "consul-provider")
public interface StudentConsumerService {
/**
* 查詢所有的學生信息
*
* @param sname
* @param age
* @return
*/
@RequestMapping(value = "/student/list",method = RequestMethod.GET)
String getStudentList(@RequestParam(value = "sname") String sname, @RequestParam(value = "age") Integer age);
}
然後:在controller
包下創建StudentConteroller
並加入如下代碼:
@Controller
@RequestMapping("/student")
@Api(value = "eureka-consumer", description = "學生查詢接口")
public class StudentConteroller {
private static final Logger LOGGER = LoggerFactory.getLogger(StudentConteroller.class);
@Autowired
StudentConsumerService studentService;
/**
* 查詢所有的學生信息
*
* @param sname
* @param age
* @param modelMap
* @return
*/
@ResponseBody
@GetMapping("/consumer/list")
public String list(
@ApiParam(value = "學生姓名") @RequestParam(required = false) String sname,
@ApiParam(value = "年齡") @RequestParam(required = false) Integer age,
ModelMap modelMap) {
String json = null;
try {
json = studentService.getStudentList(sname, age);
} catch (Exception e) {
e.printStackTrace();
modelMap.put("ren_code", "0");
modelMap.put("ren_msg", "查詢失敗===>" + e);
LOGGER.error("查詢失敗===>" + e);
json = JSON.toJSONString(modelMap);
}
return json;
}
}
接着:創建核心啓動類:
@EnableFeignClients//開啓Feign調用接口
@EnableDiscoveryClient//開啓客戶端服務,沒有這個註解會報Feign找不到對應服務的錯誤
@SpringBootApplication
public class ConsulConsumerApplication extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(EurekaConsumerApplication.class, args);
}
}
至此consul-consumer
服務提供者創建完成,瀏覽器輸入地址:http://127.0.0.1:8081/swagger-ui.htmlconsul
註冊中心可以看到服務提供和消費者都在:
跳轉看到swagger
的頁面並可是測試服務消費者的接口,可以看到如下頁面,並點擊try it out
按鈕,返回了正確的學生信息數據
返回數據如下:
{
"ren_code": "0",
"ren_msg": "查詢成功",
"studentList": [{
"age": "17",
"birth": "2010-07-22",
"dept": "王同學學習成績很不錯",
"sex": "1",
"sname": "王同學",
"sno": 1
}, {
"age": "17",
"birth": "2010-07-22",
"dept": "李同學學習成績很不錯",
"sex": "1",
"sname": "李同學",
"sno": 2
}]
}
以上就是spring cloud
基於consul
實現分佈式服務註冊與消費的整合流程,是不是很簡單,你也來小試牛刀一把吧。
源碼地址
SpringCloud核心教程 | 第四篇:服務註冊與發現 Consul篇
寫在最後
歡迎關注、喜歡、和點贊後續將推出更多的spring cloud
教程,敬請期待。
歡迎關注我的微信公衆號獲取更多更全的學習資源,視頻資料,技術乾貨!