thinkphp官方網(wǎng)站貴陽網(wǎng)站建設(shè)
文章目錄
-
- 認識微服務:
- SpringCloud
-
- 服務拆分及遠程調(diào)用
- 實現(xiàn)夸遠程服務調(diào)用使用RestTemplate
- Eureka注冊中心
-
- 搭建EruekaServer
- 注冊服務
- 服務發(fā)現(xiàn)
- Ribbon負載均衡
-
- 修改負載均衡規(guī)則
- 解餓加載
- Nacos注冊中心(nacos一部分功能)
-
- 服務注冊到nacos
- nacos服務分級存儲模型
- 環(huán)境隔離 -namespace
- Nacos配置管理
- http客戶端Feign
-
- 自定義Feign的配置
- Feign的性能優(yōu)化
- 統(tǒng)一網(wǎng)關(guān)Gateway
-
- 網(wǎng)關(guān)功能:
- 搭建網(wǎng)關(guān)服務:
- 路由斷言工廠Route Predicate Factory
- 過濾器工廠
-
- 請求頭過濾器
- 默認過濾器
- 自定義全局過濾器
- 過濾器執(zhí)行順序
- 解決跨域問題
認識微服務:
單體架構(gòu):將業(yè)務的所有功能集中在一個項目中開發(fā),打成一個包部署。
優(yōu)點:
-
部署簡單
-
部署成本低
缺點:
-
耦合度高
-
擴展性差
分布式架構(gòu):根據(jù)業(yè)務功能對系統(tǒng)進行拆分,每個業(yè)務模塊作為獨立項目開發(fā),稱為一個服務。
優(yōu)點:
-
降低服務耦合度
-
有利于服務升級擴展
缺點:
-
架構(gòu)復雜
-
難度大
微服務: 微服務是一種經(jīng)過良好架構(gòu)設(shè)計的分布式架構(gòu)方案,微服務架構(gòu)特征:
- 單一職責:微服務拆分粒度更小,每一個服務都對應唯一的業(yè)務能力,能做到單一職責,避免重復業(yè)務開發(fā)
- 面向服務:微服務對外暴露業(yè)務接口
- 自治:團隊獨立、技術(shù)獨立、數(shù)據(jù)獨立、部署獨立
- 隔離性強:服務調(diào)用做好隔離、容錯、降級、避免出現(xiàn)級聯(lián)問題
優(yōu)點: 拆分粒度更小、服務更獨立、耦合度更低
缺點: 架構(gòu)非常復雜,運維、監(jiān)控、部署難度提高
(Dubbo、SpringCloud、SpringCloudAlibaba)微服務技術(shù)對比:
企業(yè)需求:
SpringCloud
SpringCloud是目前國內(nèi)使用最廣泛的微服務架構(gòu)框架。它集成了各種微服務功能組件,并基于SpringBoot實現(xiàn)了這些組件的自動裝配。
官網(wǎng)地址:Spring Cloud
服務拆分及遠程調(diào)用
服務拆分注意事項:
- 不同微服務,不要開發(fā)相同業(yè)務
- 微服務數(shù)據(jù)獨立,不要訪問其他微服務的數(shù)據(jù)庫
- 微服務可以將自己的業(yè)務暴露為接口,提供給其他服務調(diào)用
實現(xiàn)夸遠程服務調(diào)用使用RestTemplate
通過@Bean注解將RestTemplate對象交給spring管理,在需要的地方注入RestTemplate對象
調(diào)用方法:
//利用RestTemplate發(fā)送http請求//url路徑String url = "http://localhost:8081/user/"+ order.getUserId();//發(fā)送http請求,實現(xiàn)遠程調(diào)用User user = restTemplate.getForObject(url, User.class);
Eureka注冊中心
服務調(diào)用出現(xiàn)的問題 : 1.調(diào)用服務地址硬編碼 2.多個服務如何選擇 3.怎么知道服務提供者是否是健康狀態(tài)
Eureka的作用:
-
消費者如何向服務提供者提供具體信息的
- 服務啟動時向eureka注冊自己的信息
- eureka保存信息
- 消費者根據(jù)服務名稱向eureka拉取提供者信息
-
如果多個提供者,消費者該如何選擇
- 消費者利用負載均衡算法,從服務列表中挑選一個
-
消費者如何感知服務器提供者的健康狀態(tài)
- 服務提供者每隔30秒向EruekaServer發(fā)送請求,報告健康狀態(tài)
- eureka會更新服務器列表,不正常得到提供者會被剔除
搭建EruekaServer
1.創(chuàng)建項目引入依賴
<!-- eureak服務端--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-server</artifactId></dependency>
2.編寫啟動類,添加@EnableEurekaServer注解
3.添加application.yml文件
server:port: 10086 #服務端口
spring:application:name: eurekaserver
eureka:client:service-url: #eureka的地址信息deafultZone: http://127.0.0.1:10086/eureka
啟動項目后方法10086端口就可以到eureka的界面
注冊服務
將服務注冊到eureka的步驟:
1.在服務項目中引入spring-cloud-starter-netflix-eureka-client的依賴
<!-- eureka客戶端依賴--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-client</artifactId></dependency>
2.在application.yml文件,編寫配置
server:port: 8081
spring:datasource:url: jdbc:mysql://localhost:3306/cloud_user?useSSL=falseusername: rootpassword: ******driver-class-name: com.mysql.jdbc.Driverapplication:name: userService #user服務名稱
mybatis:type-aliases-package: cn.itcast.user.pojoconfiguration:map-underscore-to-camel-case: true
logging:level:cn.itcast: debugpattern:dateformat: MM-dd HH:mm:ss:SSS
eureka:client:service-url: #eureka的地址信息defaultZone: http://127.0.0.1:10086/eureka
服務發(fā)現(xiàn)
1.在請求路徑上面把路徑換成我們注冊服務的名稱
public Order queryOrderById(Long orderId) {// 1.查詢訂單Order order = orderMapper.findById(orderId);//利用RestTemplate發(fā)送http請求//url路徑(將路徑換成需要的服務的名稱)String url = "http://userservice/user/"+ order.getUserId();//發(fā)送http請求,實現(xiàn)遠程調(diào)用User user = restTemplate.getForObject(url, User.class);order.setUser(user);// 4.返回return order;}
2.加上RestTemplate的Bean上面加 @LoadBalanced 注解
Ribbon負載均衡
ribbon的負載均衡策略
修改負載均衡規(guī)則
默認是輪詢方式
1.代碼方式:在消費端的Application中,定義一個新的IRule
@Bean
public IRule randomRule(){return new RandowRule();
}
2.配置文件方式:配置文件只是更改一個服務的負載,不是全局
userservice:ribbon:NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
解餓加載
Ribbon默認是采用懶加載,即第一次加載時才會去創(chuàng)建LoadBalanceClient,請求時間會很長。而解餓加載則會在項目啟動時創(chuàng)建,降低第一次訪問的耗時。
ribbon:eager-load:enabled: true #開啟解餓加載clients: -userservice #指定服務名稱,多個服務需要在下面加 -
Nacos注冊中心(nacos一部分功能)
Nacos是阿里巴巴的產(chǎn)品,現(xiàn)在是SpringCloud中的一個組件。相比Eureka功能更加豐富。
Windows啟動nacos命令:startup.cmd -m standalone
服務注冊到nacos
1.在Cloud-demo父工程中添加Spring-cloud-alilbaba 的管理依賴:
<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-alibaba-dependencies</artifactId><version>2.2.5.RELEASE</version><type>pom</type><scope>import</scope></dependency>
2.添加nacos的客戶端依賴
<!-- nacos客戶端依賴包--><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency>
3.改寫application.yml 文件,向yml文件中加入nacos的服務地址配置
application:name: userservice #user服務名稱cloud:nacos:discovery:server-addr: localhost:8848 #nacos服務地址
nacos服務分級存儲模型
添加服務集群屬性
修改application.yml
cloud:nacos:discovery:server-addr: localhost:8848 #nacos服務地址cluster-name: SH #集群名稱,Hz代指杭州
更改服務訪問規(guī)則(NacosRule負載均衡規(guī)則)
userservice:ribbon:NFLoadBalancerRuleClassName: com.alibaba.nacos.ribbon.NacosRule
NacosRule負載均衡規(guī)則 : 集群優(yōu)先
環(huán)境隔離 -namespace
1.在nacos網(wǎng)站去創(chuàng)建namespace命名空間
2.配置服務所在命名空間,在application.yml文件中修改
cloud:nacos:discovery:server-addr: localhost:8848cluster-name: HZnamespace: 359d6479-2271-43fa-9ca3-9cf1f1082906 #dev環(huán)境(復制我們創(chuàng)建好的命名空間Id)
不同nameSpace下的服務不可見(不能訪問)
Nacos和eureka的區(qū)別
Nacos配置管理
配置更改熱更新
統(tǒng)一配置管理
1.引入Nacos的配置管理客戶端依賴:
<!-- nacos的配置管理依賴--><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId></dependency>
2.在服務提供者中的resource目錄中添加一個bootstrap.yml文件,這個文件是引導文件,優(yōu)先級高于application.yml文件
spring:application:name: userservice #服務名稱profiles:active: dev #環(huán)境cloud:nacos:discovery:server-addr: localhost:8848 #nacos地址config:file-extension: yaml #文件后綴名
將application.yml文件中重復的配置刪掉
配置自動刷新
-
方式一:在@Value注入的變量所在類上添加注解@RefreshScope
-
方式二:使用@ConfigurationProperties注解
@Data
@Component
@ConfigurationProperties(prefix = “pattern”)
public class PatternProperties {
private String dateformat;
}
多環(huán)境配置共享優(yōu)先級:
服務名-profile.yaml > 服務名.yaml > 本地配置
熱更新的Springboot實現(xiàn)
1.導入jar包
<dependency><groupId>com.alibaba.boot</groupId><artifactId>nacos-config-spring-boot-starter</artifactId><version>0.2.11</version></dependency>
2.更改application.yaml文件
nacos:config:data-id: jc-club-oss #用于指定要獲取的配置數(shù)據(jù)的ID或名稱。secret-key: nacos # 用于進行訪問控制的密鑰,用于對配置中心的訪問進行認證和授權(quán)。access-key: nacos # 用于進行訪問控制的密鑰,用于對配置中心的訪問進行認證和授權(quán)。group: DEFAULT_GROUP #用于指定配置數(shù)據(jù)所屬的分組,這樣可以更好地組織和管理配置。type: yaml #指定配置數(shù)據(jù)的類型,例如 YAML、Properties 等。server-addr: http://117.72.14.166:8848/ #指定Nacos配置中心的地址,包括主機名和端口。auto-refresh: true #標識是否自動刷新配置內(nèi)容,當配置中心的配置發(fā)生變化時,客戶端是否自動更新配置。remote-first: true #當配置中心不可用時,是否優(yōu)先使用本地緩存的配置。bootstrap:enable: true #是否啟用Bootstrap配置,Bootstrap配置是在Spring應用程序啟動時首先加載的一組配置。
3.使用@NacosValue注解
@NacosValue(value = "${storage.service.type}",autoRefreshed = true)private String storageType;@GetMapping("/testNacos")public String testNacos() {return storageType;}
出現(xiàn) failed to req API:/nacos/v1/ns/instance after all servers([localhost:8848]) tried: java.net.ConnectException: Connection refused: connect 異常
因為同時導入了Nacos的配置中心和注冊中心的依賴導致
Nacos配置中心依賴 -config 注冊中心 -discovery
http客戶端Feign
使用之前的RestTemplate方式調(diào)用會出現(xiàn)一些問題
- 代碼可讀性差,編程體驗不統(tǒng)一
- 參數(shù)復雜URL難以維護
Feign是一聲明式的http客戶端,作用是幫助我們優(yōu)雅的實現(xiàn)http請求的發(fā)送。
1.引入Feign依賴
<!-- feign客戶端依賴-->
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId><version>2.2.7.RELEASE</version>
</dependency>
2.在啟動類上添加注解開啟Feign的功能
@MapperScan("cn.itcast.order.mapper")
@SpringBootApplication
@EnableFeignClients
3.編寫Feign客戶端
@FeignClient("userservice")
public interface UserClient {@GetMapping("/user/{id}")User findById(@PathVariable("id") Long id);
}
注意:若出現(xiàn) Load balancer does not have available server for client: userservice 錯誤 需要查看服務的命名空間是否一致
微服務之間用openfeign調(diào)用要想保證微服務之間的用戶上下信息一致需要添加feign的攔截器
@Component
public class FeignRequestInterceptor implements RequestInterceptor {@Overridepublic void apply(RequestTemplate requestTemplate) {ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();HttpServletRequest request = requestAttributes.getRequest();if (!ObjectUtils.isEmpty(request)) {String loginId = request.getHeader("loginId");if (StringUtils.isNotBlank(loginId)){requestTemplate.header("loginId",loginId);}}}
}
同時將FeignRequestInterceptor注冊成Bean交給Spring管理
@Configuration
public class FeignConfiguration {@Beanpublic RequestInterceptor requestInterceptor(){return new FeignRequestInterceptor();}
}
這樣其他微服務就可以通過攔截器拿到loginId
自定義Feign的配置
配置日志級別方式一(修改配置文件的方式):
feign:client:config:default:# 設(shè)置日志記錄級別,其取值共有none、basic、headers、fullloggerLevel: FULL
配置日志級別方式二(聲明Bean的方式):
public class DefaultFeignConfiguration {@Beanpublic Logger.Level logLevel(){return Logger.Level.BASIC;}
}
全局有效
@EnableFeignClients(defaultConfiguration = DefaultFeignConfiguration.class)
具體服務有效
@FeignClient(value = "userservice",configuration = DefaultFeignConfiguration.class)
Feign的性能優(yōu)化
使用連接池代替默認的URLConnection:
Feign添加HttpClient的支持依賴:
<!-- 引入HttpClient依賴--><dependency><groupId>io.github.openfeign</groupId><artifactId>feign-httpclient</artifactId></dependency>
配置application.yml文件:
feign:httpclient:enabled: true #支持httpClient的開關(guān)max-connections: 200 #最大連接數(shù)max-connections-per-route: 50 #單個路徑的最大連接數(shù)
Feign的優(yōu)化:
- 日志級別盡量用basic
- 使用HttpClient或者OkHttp代替URLConnection
Feign的最佳實踐
方式二:
-
新建module并引入feign的starter的依賴
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId></dependency>
-
將feign的客戶端和實體類抽取到新建的module中
-
在需要使用服務的pom文件中引入依賴
-
修改組件有關(guān)的import部分,改成使用新建module中的包
統(tǒng)一網(wǎng)關(guān)Gateway
網(wǎng)關(guān)功能:
- 身份認證和權(quán)限校驗
- 服務路由,負載均衡
- 請求限流
Springcloud 中網(wǎng)關(guān)的實現(xiàn)包括兩種:
- gateway
- zuul
搭建網(wǎng)關(guān)服務:
1.創(chuàng)建一個module,引入SpringCloudGateway的依賴和Nacos的服務發(fā)現(xiàn)依賴:
<!-- nacos服務發(fā)現(xiàn)依賴--><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency>
<!-- 網(wǎng)關(guān)gateway依賴--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-gateway</artifactId></dependency>
2.編寫路由配置以及Nacos地址
server:port: 10010 #網(wǎng)關(guān)端口
spring:application:name: gateway #服務名稱cloud:nacos:server-addr: localhost:8848gateway:routes: #網(wǎng)關(guān)路由配置- id: user-service #路由Id,自定義,只要唯一即可#uri: http://127.0.0.1:8081 #路由的目標地址http是固定地址uri: lb://userservice #路由目標地址 lb就是負載均衡,后面跟服務名稱predicates: #路由斷言,也就是判斷請求是否符合路由規(guī)則的條件- Path=/user/** #按照路徑匹配,只要以/user/開頭就符合條件- id: order-serviceuri: lb://orderservicepredicates: - Path=/order/**
總結(jié):
網(wǎng)關(guān)搭建步驟:
-
創(chuàng)建項目,引入nacos服務發(fā)現(xiàn)和gateway依賴
-
配置application.yml,包括服務基本信息、nacos地址、路由
路由配置包括:
-
路由id:路由的唯一標示
-
路由目標(uri):路由的目標地址,http代表固定地址,lb代表根據(jù)服務名負載均衡
-
路由斷言(predicates):判斷路由的規(guī)則,
-
路由過濾器(filters):對請求或響應做處理
路由斷言工廠Route Predicate Factory
我們在配置文件中寫的斷言規(guī)則只是字符串,這些字符串會被Predicate Factory讀取并處理,轉(zhuǎn)變?yōu)槁酚膳袛嗟臈l件
例如Path=/user/**是按照路徑匹配,這個規(guī)則是由
org.springframework.cloud.gateway.handler.predicate.PathRoutePredicateFactory
類來
處理的,像這樣的斷言工廠在SpringCloudGateway還有十幾個:
名稱
說明
示例
After
是某個時間點后的請求
- After=2037-01-20T17:42:47.789-07:00[America/Denver]
Before
是某個時間點之前的請求
- Before=2031-04-13T15:14:47.433+08:00[Asia/Shanghai]
Between
是某兩個時間點之前的請求
- Between=2037-01-20T17:42:47.789-07:00[America/Denver], 2037-01-21T17:42:47.789-07:00[America/Denver]
Cookie
請求必須包含某些cookie
- Cookie=chocolate, ch.p
Header
請求必須包含某些header
- Header=X-Request-Id, d+
Host
請求必須是訪問某個host(域名)
- Host=.somehost.org,.anotherhost.org
Method
請求方式必須是指定方式
- Method=GET,POST
Path
請求路徑必須符合指定規(guī)則
- Path=/red/{segment},/blue/**
Query
請求參數(shù)必須包含指定參數(shù)
- Query=name, Jack或者- Query=name
RemoteAddr
請求者的ip必須是指定范圍
- RemoteAddr=192.168.1.1/24
Weight
權(quán)重處理
過濾器工廠
GatewayFilter是網(wǎng)關(guān)中提供的一種過濾器,可以對進入網(wǎng)關(guān)的請求和微服務返回的響應做處理:
名稱
說明
AddRequestHeader
給當前請求添加一個請求頭
RemoveRequestHeader
移除請求中的一個請求頭
AddResponseHeader
給響應結(jié)果中添加一個響應頭
RemoveResponseHeader
從響應結(jié)果中移除有一個響應頭
RequestRateLimiter
限制請求的流量
請求頭過濾器
下面我們以AddRequestHeader 為例來講解。
需求:給所有進入userservice的請求添加一個請求頭:Truth=itcast is freaking awesome!
只需要修改gateway服務的application.yml文件,添加路由過濾即可:
spring:cloud:gateway:routes:- id: user-service uri: lb://userservice predicates: - Path=/user/** filters: # 過濾器- AddRequestHeader=Truth, Itcast is freaking awesome! # 添加請求頭
當前過濾器寫在userservice路由下,因此僅僅對訪問userservice的請求有效。
默認過濾器
如果要對所有的路由都生效,則可以將過濾器工廠寫到default下。格式如下:
spring:cloud:gateway:routes:- id: user-service uri: lb://userservice predicates: - Path=/user/**default-filters: # 默認過濾項- AddRequestHeader=Truth, Itcast is freaking awesome!
自定義全局過濾器
需求:定義全局過濾器,攔截請求,判斷請求的參數(shù)是否滿足下面條件:
-
參數(shù)中是否有authorization,
-
authorization參數(shù)值是否為admin
如果同時滿足則放行,否則攔截
實現(xiàn):
在gateway中定義一個過濾器:
package cn.itcast.gateway.filters;import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;@Order(-1)
@Component
public class AuthorizeFilter implements GlobalFilter {@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {// 1.獲取請求參數(shù)MultiValueMap<String, String> params = exchange.getRequest().getQueryParams();// 2.獲取authorization參數(shù)String auth = params.getFirst("authorization");// 3.校驗if ("admin".equals(auth)) {// 放行return chain.filter(exchange);}// 4.攔截// 4.1.禁止訪問,設(shè)置狀態(tài)碼exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN);// 4.2.結(jié)束處理return exchange.getResponse().setComplete();}
}
過濾器執(zhí)行順序
請求進入網(wǎng)關(guān)會碰到三類過濾器:當前路由的過濾器、DefaultFilter、GlobalFilter
請求路由后,會將當前路由過濾器和DefaultFilter、GlobalFilter,合并到一個過濾器鏈(集合)中,排序后依次執(zhí)行每個過濾器:
排序的規(guī)則:
- 每一個過濾器都必須指定一個int類型的order值,order值越小,優(yōu)先級越高,執(zhí)行順序越靠前。
- GlobalFilter通過實現(xiàn)Ordered接口,或者添加@Order注解來指定order值,由我們自己指定
- 路由過濾器和defaultFilter的order由Spring指定,默認是按照聲明順序從1遞增。
- 當過濾器的order值一樣時,會按照 defaultFilter > 路由過濾器 > GlobalFilter的順序執(zhí)行。
詳細內(nèi)容,可以查看源碼:
org.springframework.cloud.gateway.route.RouteDefinitionRouteLocator#getFilters()
方法是先加載defaultFilters,然后再加載某個route的filters,然后合并。
org.springframework.cloud.gateway.handler.FilteringWebHandler#handle()
方法會加載全局過濾器,與前面的過濾器合并后根據(jù)order排序,組織過濾器鏈
解決跨域問題
在gateway服務的application.yml文件中,添加下面的配置:
spring:cloud:gateway:# 。。。globalcors: # 全局的跨域處理add-to-simple-url-handler-mapping: true # 解決options請求被攔截問題corsConfigurations:'[/**]':allowedOrigins: # 允許哪些網(wǎng)站的跨域請求 - "http://localhost:8090"allowedMethods: # 允許的跨域ajax的請求方式- "GET"- "POST"- "DELETE"- "PUT"- "OPTIONS"allowedHeaders: "*" # 允許在請求中攜帶的頭信息allowCredentials: true # 是否允許攜帶cookiemaxAge: 360000 # 這次跨域檢測的有效期
網(wǎng)關(guān)提示503錯誤,并且配置的地址信息都符合 查看是否配置的負載均衡的依賴
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-loadbalancer</artifactId></dependency>