目錄
4.應用監控與管理:actuator (可以放在任何一個springboot項目上)
5.客戶端之間的調用feign(Feign 是對 Ribbon的封裝,使用註解的方式,調用起來更簡單。。。 也是主流的方式~)
6.客戶端之間的調用Ribbon(Ribbon 是使用 restTemplate 進行調用,並進行客戶端負載均衡。)
7.熔斷器:hystrix(熔斷只是作用在服務調用這一端,與feign一塊使用)
一.SpringCloud簡介
springcloud是微服務架構,他是由多個獨立項目集合而成的,每個項目都是獨立的,各自進行自己的迭代和版本發佈。所以springcloud不方便使用版本號來管理,而是使用版本名。以避免和子項目版本號的衝突。
二.常用組成
- 註冊中心:eureak(除了eureak springcloud還提供了Consul服務中心)
- 客戶端:client
- 配置中心:config
- 應用監控與管理:actuator
- 客戶端之間的調用:feign
- 客戶端負載均衡:ribbon
- 熔斷器:hystrix
- 服務網關: Zuul,Geteway
- 消息驅動:Stream
- 服務鏈路追蹤:Zipkin
- 消息總線:Bus
- 批量任務:Task
- 授權認證:SpringSecurity
三.代碼實現
1.註冊中心Eureak
- 創建一個springboot項目,裏面啥組件都不許需要添加,或者用默認的eureak組件,我是搭建了一空的架子,一個一個添
- 添加pom
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
-------------------------------------------------------------------------------
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.SR3</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
- 在啓動類添加註解,來標識這是一個eureak服務
@EnableEurekaServer
- 配置yml,單機配置,後面有其他的會在下面添加
server:
port: 8010
spring:
application:
name: eureka-server
eureka:
#指定主機名稱
instance:
hostname: 127.0.0.1
#server一定程度上也是client,互爲client,
client:
#由於自己就是服務器,不需要註冊到自己
register-with-eureka: false
#由於自己就是服務器,不需要從服務器獲取註冊信息
fetch-registry: false
#服務地址
service-url:
defaultZone: http://127.0.0.1:8010/eureka/
- 啓動訪問:http://localhost:8010,出現下面的界面說明基本eureak搭建好了
- 添加security賬號認證,在pom中添加security包
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-security -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
<version>2.1.8.RELEASE</version>
</dependency>
- 在yml中加入
server:
port: 8010
spring:
application:
name: eureka-server
security:
basic:
enabled: true
user:
name: herbert
password: 123456
eureka:
#指定主機名稱
instance:
hostname: 127.0.0.1
#server一定程度上也是client,互爲client,
client:
#由於自己就是服務器,不需要註冊到自己
register-with-eureka: false
#由於自己就是服務器,不需要從服務器獲取註冊信息
fetch-registry: false
#服務地址
service-url:
defaultZone: http://${spring.security.user.name}:${spring.security.user.password}@${eureka.instance.hostname}:${server.port}/eureka/
- 加入一個CSRF禁用問題,這是一個坑,大坑,查資料瞭解到新版(Spring Cloud 2.0 以上)的security默認啓用了csrf檢驗,要在eurekaServer端配置security的csrf檢驗爲false
@EnableWebSecurity
class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().ignoringAntMatchers("/eureka/**");
super.configure(http);
}
}
2.客戶端client
- 創建一個springboot項目,裏面啥組件都不許需要添加,或者用默認的eureak組件,我是搭建了一空的架子,一個一個添
- 添加pom
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
-------------------------------------------------------------------------------
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.SR3</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
- 在啓動類添加以下註解標識客戶端
@EnableEurekaClient
- 配置yml文件,現在連接的是沒有security的服務端
server:
port: 8005
spring:
application:
name: client-student-one
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:8010/eureka/
- 訪問服務端eureak地址:http://localhost:8010,會出現下面紅線的地方
- 配置yml文件,現在連接的是有security的服務端
server:
port: 8005
spring:
application:
name: client-student-one
eureka:
client:
service-url:
defaultZone: http://herbert:[email protected]:8010/eureka/
- 客戶端自此配置完成,可以多配置幾個客戶端,方便後期的學習,我這邊還添加了
server:
port: 8006
spring:
application:
name: client-student-two
eureka:
client:
service-url:
defaultZone: http://herbert:[email protected]:8010/eureka/
---------------------------------------------------------------------------------
server:
port: 8007
spring:
application:
name: client-student-three
eureka:
client:
service-url:
defaultZone: http://herbert:[email protected]:8010/eureka/
3.配置中心Config
- 添加pom
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
-------------------------------------------------------------------------------
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.SR3</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
- 再啓動類加入以下配置來標記是配置中心
@EnableConfigServer
- 配置yml,看下圖我的幾個配置
(1)application-client.yml
eureka:
client:
service-url:
defaultZone: http://herbert:[email protected]:8010/eureka/
(2)application-eureka.yml
server:
port: 8010
spring:
application:
name: eureka-server
security:
basic:
enabled: true
user:
name: herbert
password: 123456
eureka:
instance:
hostname: 127.0.0.1
client:
register-with-eureka: false
fetch-registry: false
service-url:
defaultZone: http://${spring.security.user.name}:${spring.security.user.password}@${eureka.instance.hostname}:${server.port}/eureka/
(3)application.yml
server:
port: 8102
spring:
cloud:
config:
server:
native:
search-locations: classpath:/server/
application:
name: eureka-config
profiles:
active: native
- eureka config-server就搭建完了,我們來測試一下,項目跑起來,輸入:http://127.0.0.1:8102/server/client,如下圖就搭建完了
- 或者輸入:http://127.0.0.1:8102/server/application-client.yml出現以下內容,就說明搭建成功
- 下一步就是讓客戶端鏈接我們的服務中心,這裏有一個大坑,就是springboot讀取配置文件的順序,這裏要把application.yml改成bootstrap.yml , 我這裏將application.yml改成了bootstrap.yml
(1)加入pom的包
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
(2)配置bootstrap.yml
spring:
cloud:
config:
label: server
profile: client
uri: http://localhost:8102
server:
port: 8007
將所有的springboot都修改了,連接一個config,修改完後可以將config-server中的配置加載在其他服務裏面
4.應用監控與管理:actuator (可以放在任何一個springboot項目上)
- 新建一個springboot項目,查看一下
- 添加pom
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
- 修改yml端口爲 8011
server:
port: 8011
management:
endpoints:
web:
exposure:
include: "*" #暴露所有端點 默認是info,health
- 啓動訪問 衆多 REST 接口、遠程 shell 和 JMX 獲得
- 例如:訪問健康值 http://localhost:8011/actuator/health
{"status":"UP"} 是健康狀態
具體可以參考一下下面的接口
其他的具體操作可以參考:https://blog.csdn.net/wya1993/article/details/80540981
5.客戶端之間的調用feign(Feign 是對 Ribbon的封裝,使用註解的方式,調用起來更簡單。。。 也是主流的方式~)
- 我們用客戶端2通過feign調用客戶端1的接口
在客戶端2中引入pom
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-openfeign -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>2.1.3.RELEASE</version>
</dependency>
加入feign啓動註解,表示我是一個feign客戶端
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableFeignClients
@EnableEurekaClient
public class ClientApplication {
public static void main(String[] args) {
SpringApplication.run(ClientApplication.class, args);
}
}
調用客戶1的註解,首先在客戶端1寫一個接口
客戶端2開始調用這個接口了,通過接口調用,其中@FeigenClient裏面的迷你必須是註冊中心的名字,在http://localhost:8010/eureak服務中心查找
最後就是在controller調用這個接口,調用成功
訪問這個接口調用成功
6.客戶端之間的調用Ribbon(Ribbon 是使用 restTemplate 進行調用,並進行客戶端負載均衡。)
- 我們還是用客戶端2和客戶端1做測試,首先在客戶端1中寫一個接口爲
- 我們用客戶端2通過ribbon調用客戶端1的ribbon接口
1.在客戶端2添加pom
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
2.我們初始化restTemplate (RestTemplate 是由 Spring Web 模塊提供的工具類,與 SpringCloud 無關,是獨立存在的因 SpringCloud 對 RestTemplate 進行了一定的擴展,所以 只注入實例化時被@LoadBalanced修飾的實例。)再啓動類中加入
@Bean
@LoadBalanced
RestTemplate restTemplate() {
return new RestTemplate();
}
3.調用客戶端1的ribbon接口
這就是ribbon的調用,個人覺得還不如用feign
7.熔斷器:hystrix(熔斷只是作用在服務調用這一端,與feign一塊使用)
- 我們繼續用客戶端2調用客戶端1的接口,使用feign+hystrix
1.現在客戶端1寫一個接口
2.在客戶端2的yml中開啓hystrix
feign:
hystrix:
enabled: true
3.因爲hystrix和feign一塊用,一個客戶端不能出現兩個接口連接同一個feign,所以我們在上面的feign上修改
修改FeignService接口
添加ProductClientFeignHystrix類
@Component
public class ProductClientFeignHystrix implements FeignService {
@Override
public String getFrignHystrix() {
return "這個接口錯了,現在到了這個接口這就是hystrix";
}
@Override
public String getFeign() {
return "這個接口錯了,現在到了這個接口這就是hystrix";
}
}
在FeignController添加api接口
測試:你先訪問http://localhost:8006/feignHystrix 然後再把客戶端1斷開,你在訪問自己可以試試兩次結果
8.網關
zuul和getway的區別和選擇
Spring Cloud Gateway 是 Spring Cloud 微服務平臺的一個子項目,屬於 Spring 開源社區,依賴名叫:spring-cloud-starter-gateway。它是 Spring Cloud 的一個全新項目,該項目是基於 Spring 5.0,Spring Boot 2.0 和 Project Reactor 等技術開發的網關,它旨在爲微服務架構提供一種簡單有效的統一的 API 路由管理方式。Spring Cloud Gateway 作爲 Spring Cloud 生態系統中的網關,目標是替代 Netflix Zuul,其不僅提供統一的路由方式,並且基於 Filter 鏈的方式提供了網關基本的功能,例如:安全,監控/指標,和限流。
Spring Cloud Gateway 的特徵:
- 基於 Spring Framework 5,Project Reactor 和 Spring Boot 2.0
- 動態路由
- Predicates 和 Filters 作用於特定路由
- 集成 Hystrix 斷路器
- 集成 Spring Cloud DiscoveryClient
- 易於編寫的 Predicates 和 Filters
- 限流
- 路徑重寫
Zuul 是 Netflix 公司的開源項目,Spring Cloud 在 Netflix 項目中也已經集成了 Zuul,依賴名叫:spring-cloud-starter-netflix-zuul。它提供了一個框架,可以對過濾器進行動態的加載,編譯,運行。過濾器之間沒有直接的相互通信。他們是通過一個RequestContext的靜態類來進行數據傳遞的。RequestContext類中有ThreadLocal變量來記錄每個Request所需要傳遞的數據。過濾器是由Groovy寫成。這些過濾器文件被放在Zuul Server上的特定目錄下面。Zuul會定期輪詢這些目錄。修改過的過濾器會動態的加載到Zuul Server中以便於request使用。
Zuul可以通過加載動態過濾機制,從而實現以下各項功能:
- 驗證與安全保障: 識別面向各類資源的驗證要求並拒絕那些與要求不符的請求。
- 審查與監控: 在邊緣位置追蹤有意義數據及統計結果,從而爲我們帶來準確的生產狀態結論。
- 動態路由: 以動態方式根據需要將請求路由至不同後端集羣處。
- 壓力測試: 逐漸增加指向集羣的負載流量,從而計算性能水平。
- 負載分配: 爲每一種負載類型分配對應容量,並棄用超出限定值的請求。
- 靜態響應處理: 在邊緣位置直接建立部分響應,從而避免其流入內部集羣。
- 多區域彈性: 跨越AWS區域進行請求路由,旨在實現ELB使用多樣化並保證邊緣位置與使用者儘可能接近。
作者:一個會寫詩的程序員
鏈接:https://www.jianshu.com/p/e0434a421c03
來源:簡書
著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。
一、Zuul(俗稱springcloud的看門狗)
1.新建一個springboot2.0x的空架子,在pom中引入
<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>
<version>2.1.2.RELEASE</version>
</dependency>
--------------------------------------------------------
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.RC2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
2.再啓動類加入註解
@EnableZuulProxy
@EnableEurekaClient
3.配置yml文件
server:
port: 8017
eureka:
client:
serviceUrl:
defaultZone: http://herbert:[email protected]:8010/eureka/
spring:
application:
name: eureka-zuul
zuul:
routes:
api-a:
path: /api-one/**
serviceId: CLIENT-STUDENT-ONE
api-b:
path: /api-two/**
serviceId: CLIENT-STUDENT-TWO
4.現在基本的zuul就搭建起來了。把項目跑起來訪問client1的feign接口
原來訪問是:localhost:8005/feign
現在通過zuul訪問:localhost:8017/api-one/feign 就可以了
5.寫zuulFilter
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
/**
* @Author:hemingzhu
* @date: 2019/11/23 10:27
* @Explanation:
*/
@Component
public class ZuulFiterTest extends ZuulFilter {
@Override
public String filterType() {
System.out.println("11111");
return "pre"; // 可以在請求被路由之前調用
}
@Override
public int filterOrder() {
// filter執行順序,通過數字指定 ,優先級爲0,數字越大,優先級越低
return 0;
}
@Override
public boolean shouldFilter() {
// 是否執行該過濾器,此處爲true,說明需要過濾
return true;
}
@Override
public Object run() throws ZuulException {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
System.out.println(("--->>> TokenFilter {},{}"+ request.getMethod()+"->"+request.getRequestURL().toString()));
String token = request.getParameter("token");// 獲取請求的參數
if (StringUtils.isNotBlank(token)) {
ctx.setSendZuulResponse(true); //對請求進行路由
ctx.setResponseStatusCode(200);
ctx.set("isSuccess", true);
return null;
} else {
ctx.setSendZuulResponse(false); //不對其進行路由
ctx.setResponseStatusCode(400);
ctx.setResponseBody("token is empty");
ctx.set("isSuccess", false);
return null;
}
}
}
過濾器這就成功了
6.回退機制fallback
package com.herbert.eureak.zuul.fallback;
import org.springframework.cloud.netflix.zuul.filters.route.FallbackProvider;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.stereotype.Component;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
/**
* @Author:hemingzhu
* @date: 2019/11/23 10:54
* @Explanation:
*/
@Component
public class ZuulFallBack implements FallbackProvider {
@Override
public String getRoute() {
return null; //服務id,可以用* 或者 null 代表所有服務都過濾
}
@Override
public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
return new ClientHttpResponse() {
@Override
public HttpStatus getStatusCode() throws IOException {
return HttpStatus.OK; //請求網關成功了,所以是ok
}
@Override
public int getRawStatusCode() throws IOException {
return HttpStatus.OK.value();
}
@Override
public String getStatusText() throws IOException {
return HttpStatus.OK.getReasonPhrase();
}
@Override
public void close() {
}
@Override
public InputStream getBody() throws IOException {
return new ByteArrayInputStream("錯誤".getBytes("UTF-8")); //返回前端的內容
}
@Override
public HttpHeaders getHeaders() {
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.APPLICATION_JSON_UTF8); //設置頭
return httpHeaders;
}
};
}
}
測試:我們將客戶端掛了訪問 ----》 localhost:8017/api-one/feign?token=1 (token主要是過filter的,如果不寫將filter屏蔽了就好)
zuul我們就說到這裏
二、getway
---------------------------------------------------後續慢慢更新------------------------------------------------------