##項目概要html
項目環境信息
IDEA ultimate 2018.3.2 springboot 2.1.7.RELEASE springCloud Greenwich.SR2 前端
###Eureka 介紹java
基於netflix eureka作了二次封裝 兩個組件組成:mysql
- Eureka Server 註冊中心
- Eureka Client 服務註冊
搭建Eureka Server
一、配置Eureka 的application.ymlgit
eureka: client: serviceUrl: defaultZone: http://localhost:8761/eureka/ #默認爲true,是否向本身註冊本身 register-with-eureka: false #關閉Eureka自我保護(生產環境要設置爲true,測試環境設置爲false) server: enable-self-preservation: false #配置服務實例名 spring: application: name: spring-cloud-eureka #配置服務實例端口 server: port: 8761
注意:若是報錯讀取application.yml文件錯誤,檢查Settings->File Encodings,所有設置爲UTF-8 二、配置Eureka pom.xmlgithub
<?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>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.7.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.practice</groupId> <artifactId>eureka</artifactId> <version>0.0.1-SNAPSHOT</version> <name>eureka</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> <spring-cloud.version>Greenwich.SR2</spring-cloud.version> </properties> <dependencies> <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-test</artifactId> <scope>test</scope> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
三、Eureka 入口類web
package com.practice.eureka; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer; @SpringBootApplication @EnableEurekaServer public class EurekaApplication { public static void main(String[] args) { SpringApplication.run(EurekaApplication.class, args); } }
四、Eureka的高可用算法
設置啓動端口在VM options設置啓動端口 -Dserver.port=8761
啓動多個能夠以下配置,相互註冊 spring
Eureka Server有心跳檢測、健康檢查、負載均衡等功能。sql
能夠啓動多個Eureka Server,互相註冊(經過修改端口啓動多個Eureka Server,修改註冊中心地址爲對方實現互相註冊) 好比,啓動了Eureka Server1 和 Eureka Server2 這樣Eureka Client在註冊到其中一臺Eureka Server後(如Server1),也會同步到另外一個Eureka Server。 若是Eureka Server1掛掉了,另外一臺Eureka Server2仍然可用,並能夠發現客戶端。
可是若是從新啓動Eureka Client,則會發現因爲Server1已經停掉了,則沒法正常註冊,也就沒法同步到Server2
爲了確保高可用,能夠將Client同時註冊到全部的Eureka上
-註冊中心地址配置 eureka: client: service-url: defaultZone: http://localhost:8761/eureka,http://localhost:8762/eureka
若是超過兩臺以上的Eureka Server,爲保證高可用,則能夠設置其兩兩註冊,同時將Client同時註冊到全部Eureka Server上。
註冊發現
一、客戶端入口類 添加EnableDiscoveryClient,提供eureka客戶端
package com.practice.client; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; @SpringBootApplication @EnableDiscoveryClient public class ClientApplication { public static void main(String[] args) { SpringApplication.run(ClientApplication.class, args); } }
二、Client新建application.yml
eureka: client: serviceUrl: defaultZone: http://localhost:8761/eureka/ #頁面跳轉後臺端口前名稱 # instance: # hostname: product #客戶端實例在註冊中心的名稱 spring: application: name: product # mysql數據庫鏈接信息 datasource: driver-class-name: com.mysql.jdbc.Driver username: root password: 123456 url: jdbc:mysql://127.0.0.1:3306/springcloud_sell?characterEncoding=utf-8&useSSL=false # 打開jpa日誌,在控制檯打印 jpa: show-sql: true env: dev
三、Client新建pom.xml
<?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>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.7.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.practice</groupId> <artifactId>client</artifactId> <version>0.0.1-SNAPSHOT</version> <name>client</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> <spring-cloud.version>Greenwich.SR2</spring-cloud.version> </properties> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
四、客戶端書寫中間實體(*) 下面的JsonProperty中的value是序列化後的名稱,定義的名稱是爲了更好的理解屬性
@Data public class ProductVO { /** * 此處須要返回給前端name,可是表示的是類目名稱,因此添加JsonProperty進行標識 */ @JsonProperty("name") private String categoryName; @JsonProperty("type") private Integer categoryType; @JsonProperty("foods") List<ProductInfoVO> productInfoVOList; }
Ribbon實現負載均衡
一、瞭解Ribbon
Eureka屬於客戶端發現,客戶端會向服務器拉取可用的服務器信息,根據負載均衡服務,命中那臺服務器發送請求,整個過程都是在客戶端完成,並不須要服務器的參與。
Ribbon是netflix ribbon實現的,經過springcloud的封裝,輕鬆的面向服務的rest的模板請求,自動的轉換爲客戶端服務調用。
RestTemplate/zuul/Feign都使用到了Ribbon
SpringCloud在結合Ribbon的負載均衡實踐中,封裝增長了HTTPClient和OKHttpClient 兩種請求端實現,默認使用了Ribbon對Eureka的客戶端發現的負載均衡client
二、Ribbon的工做原理
輪尋RoundRobinRule、隨機鏈接RandomRule
Ribbon實現軟負載均衡核心有三點
(1)服務發現:依據實例名稱,把該實例下全部的實例都找出來
(2)服務選擇規則:依據規則從多個規則中選擇有效的服務
(3)服務監聽:檢測失效的服務,作到高效剔除
Ribbon主要組件
serverList (獲取全部的可用服務列表)----> ServerListFilter(過濾掉一部分地址) -----> IRule從剩下的地址中選擇一個實例,做爲目標結果。
HTTP服務調用方式RestTemplate 操做和Feign簡單對比
RestTemplate使用的三種方式
詳解restTemplate操做 https://blog.csdn.net/itguangit/article/details/78825505
方式一:直接使用restTemplate,路徑固定了很差修改 restTemplate對於路徑的訪問比較固定,若是線上部署,很差進行修改;或者是啓動了多個實例,很差進行捕獲和調用
RestTemplate restTemplate = new RestTemplate(); String response = restTemplate.getForObject("http://localhost:9001/msg", String.class); log.info("response{}",response); return response;
方式二:利用LoadBalancerClient經過應用名獲取url,而後在使用restTemplate 注入LoaderBalancerClient,經過服務名稱進行獲取服務的ip和端口號
@Autowired private LoadBalancerClient loadBalancerClient; @GetMapping("/getProductMsg") public String getProductMsg(){ // 方式二:(利用LoadBalancerClient經過應用名獲取url,而後在使用restTemplate) RestTemplate restTemplate = new RestTemplate(); ServiceInstance serviceInstance = loadBalancerClient.choose("PRODUCT"); String url = String.format("http://%s:%s", serviceInstance.getHost(),serviceInstance.getPort() + "/msg"); String response = restTemplate.getForObject(url, String.class); log.info("response{}",response); return response; }
方式三:經過LoadBalanced實例化restTemplate
@Component public class RestTemplateConfig { @Bean @LoadBalanced public RestTemplate restTemplate() { return new RestTemplate(); } }
經過實例名稱進行訪問,隨機分配一個服務
String response = restTemplate.getForObject("http://PRODUCT/msg", String.class);
Feign進行通訊
- 聲明式Rest客戶端(僞RPC)
- 採用了基於接口的註解
第一步:引入pom.xml依賴
<!--應用間通訊--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-feign</artifactId> </dependency>
第二步:添加EnableFeignClient註解
第三步:經過@FeignClient聲明被調用的服務信息
@FeignClient(value = "product", fallback = ProductClientFallbackImpl.class ) public interface ProductClient { /** * 獲取產品端的信息 * * @return */ @GetMapping("/msg") String getProductMsg(); }
經過fallback處理異常等信息
@Component @Slf4j public class ProductClientFallbackImpl implements ProductClient { /** * 獲取產品端的信息 * * @return */ @Override public String getProductMsg() { log.info("返回消息接口出現問題"); return null; } }
Config配置中心
爲何須要統一配置中心? 不方便維護 配置內容安全與權限 更新配置項目須要重啓
Config Server服務端配置
一、Config服務引入pom.xml
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-config-server</artifactId> </dependency>
二、Config服務配置yml文件
spring: application: name: micro-weather-config-server cloud: config: server: git: ## 統一配置的倉庫 uri: https://github.com/MarkGao11520/spring-cloud-repo/ username: xxxx password: xxxx # 拉取下來的文件防止位置 basedir: /Users/gaowenfeng/project/idea/springcloud_sell/config/basedir ## 倉庫路徑 search-paths: config-repo eureka: # 服務的url service-url: defaultZone: http://localhost:8761/eureka/ server: port: 8888
配置中心須要配置遠程git倉庫存儲配置 配置中心獲取配置文件的兩種方式: /{name}-{profiles}.yml /{label}/{name}-{profiles}.yml
name 服務名 profiles 環境 label 分支(branch)
第一種默認使用的是master分支
三、Config啓動類上引入ConfigServer註解
@SpringBootApplication @EnableEurekaClient @EnableConfigServer public class ConfigApplication { public static void main(String[] args) { SpringApplication.run(ConfigApplication.class, args); } }
Config Client客戶端配置
一、order客戶端pom.xml
<!--config配置中心--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-config-client</artifactId> </dependency>
1.這裏若是配置文件名稱爲application.xxx的話,會搞亂順序,先找配置中心再找註冊中心,這樣會找不到,會致使去加載默認的8888 2. eureka.service-url的配置須要在本地,否則會找不到註冊中心 bootstrap.yml中配置
# 配置規則 #因此應該叫bootstrap.yml # /{application}/{profile}[/{label}] # /{application}-{profile}.yml # /{application}-{profile}.properties # /{label}/{application}-{profile}.yml # /{label}/{application}-{profile}.properties spring: application: # 配置規則裏的{application} name: micro-weather-config-client cloud: config: # 找到配置的方式一 # uri: http://localhost:8888 # 配置規則裏的${profile} # profile: bus-dev # 配置規則裏的${label},label 是分支,不是路徑! # label: test # 找到配置的方式二 discovery: enabled: true service-id: CONFIG profile: test eureka: # 服務的url service-url: defaultZone: http://localhost:8761/eureka/
測試
@RunWith(SpringRunner.class) @SpringBootTest public class MicroWeatherConfigClientApplicationTests { @Value("${version}") private String version; @Test public void contextLoads() { Assert.assertEquals("1.0",version); } }
配置完成以後經過
localhost:8989/order-dev.yml
進行訪問
經過localhost:8989/bus/refresh
進行動態刷新本地的配置信息
配置中心出現的問題:
a、springcloud 消息總線讀取配置文件的兩種方式
https://blog.csdn.net/qq_35275233/article/details/89074886
b、springcloud監控配置中心健康狀態
https://blog.csdn.net/qq_35275233/article/details/89074380
c、SpringCloud 配置中心執行bus/refresh 出現 Full authentication is required to access this resource
Spring Cloud Bus 自動刷新配置
一、添加依賴
rabbitmq 的bus依賴添加 config server 和client的版本應該對應起來才能夠 <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-bus-amqp</artifactId> </dependency>
二、將bus-refresh接口暴露出去
## 變量名可能會變,利用自動提示能夠找到對應的正確配置變量 (輸入management.endpoints 找到默認包含一個[health,info]數組的變量,修改成"*") management: endpoints: web: 暴露全部接口 expose: "*"
三、修改使用配置的客戶端的地方
@RestController @RequestMapping("/env") @RefreshScope // 加入這個註解自動刷新配置 public class EnvController { @Value("${env}") private String env; @GetMapping("/print") public String print() { return env; } } } @Data @Component @ConfigurationProperties("girl") @RefreshScope // 加入這個註解自動刷新配置 public class GirlConfig { private String name; private Integer age; }
四、使用
修改git的配置之後,POST 請求訪問 "http://192.161.4:8080/actuator/bus-refresh" 便可(這個路徑可能會變,具體請查閱啓動日誌)
五、集成WebHooks實現動態更新
在GitHub或者其餘git工具上配置webhook,而後就能夠實現,在git更新的時候,發送請求給咱們的config服務器讓其刷新配置
經過RabbitMQ給各服務發送消息
一、發送消息
/** * 測試給家電和水果服務發送消息 */ @Test public void sendOrderMqMsg() { amqpTemplate.convertAndSend("myOrder","computer","now:" + new Date()); }
二、設置消息監聽
/** * 1/測試給家電服務發送消息 * @param message */ @RabbitListener(bindings = @QueueBinding( exchange = @Exchange("myOrder"), key = "computer", value = @Queue("computerOrder") )) public void processComputer (String message) { log.info("ComputerOrder:{}",message); } /** * 2/測試給水果服務發送消息 * @param message */ @RabbitListener(bindings = @QueueBinding( exchange = @Exchange("myOrder"), key = "fruit", value = @Queue("fruitOrder") )) public void processFruit (String message) { log.info("FruitOrder:{}",message); }
Spring Cloud Stream
官方定義stream爲微服務應用構建消息驅動能力的框架,爲springcloud的另外一個組件
目前springcloud支持的消息中間件是RabbitMQ和Kafka 一、引入pom.xml
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-stream-rabbit</artifactId> </dependency>
服務網關和Zuul
核心依賴
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-zuul</artifactId> </dependency>
啓動類
/** * Eureka 客戶端啓動類 * 啓用Eureka客戶端 */ @SpringBootApplication @EnableDiscoveryClient @EnableZuulProxy public class MicroWeatherEurekaClientApplication { public static void main(String[] args) { SpringApplication.run(MicroWeatherEurekaClientApplication.class, args); } }
配置
spring: application: name: micro-weather-eureka-client eureka: # 服務的url service-url: defaultZone: http://localhost:8761/eureka/ zuul: routes: # 將city的請求轉發到應用msa-weather-city-server city: path: /city/** serviceId: msa-weather-city-server # 將data的請求轉發到應用msa-weather-data-server data: path: /data/** serviceId: msa-weather-data-server
路由 + 過濾器 = zuul
除了spring bus外,zuul也能夠實現自動刷新配置
/** * 實現配置的動態注入 */ @Component public class ZuulConfig { @ConfigurationProperties("zuul") @RefreshScope public ZuulProperties zuulProperties() { return new ZuulProperties(); } }
Zuul的高可用
- 多個Zuul節點註冊到Eureka Server
- Nginx和Zuul的「混搭」
pre和post過濾器
一、訪問前進行參數校驗
pre-filter:請求到目標結果以前,對返回的結果進行加工,訪問鏈接中須要添加token,進行接口加密校驗
@Component public class TokenFilter extends ZuulFilter { @Override public String filterType() { return PRE_TYPE; } @Override public int filterOrder() { return PRE_DECORATION_FILTER_ORDER - 1; } @Override public boolean shouldFilter() { return true; } @Override public Object run() { RequestContext requestContext = RequestContext.getCurrentContext(); HttpServletRequest request = requestContext.getRequest(); String token = request.getParameter("token"); if (StringUtils.isEmpty(token)) { requestContext.setSendZuulResponse(false); requestContext.setResponseStatusCode(HttpStatus.SC_UNAUTHORIZED); } return null; } }
二、post-fileter : 請求到目標結果以後,對返回的結果進行加工
@Component public class AddResponseHeaderFilter extends ZuulFilter { @Override public String filterType() { return POST_TYPE; } @Override public int filterOrder() { return SEND_RESPONSE_FILTER_ORDER - 1; } @Override public boolean shouldFilter() { return true; } @Override public Object run() { /** * 往返回的結果添加x-foo */ RequestContext requestContext = RequestContext.getCurrentContext(); HttpServletResponse response = requestContext.getResponse(); response.setHeader("X-FOO", UUID.randomUUID().toString()); return null; } }
Zuul 限流
時機:請求被轉發以前調用
令牌桶:拿到令牌的放行
限流過濾器
/** * 限流過濾器 */ @Component public class RateLimitFilter extends ZuulFilter { /** * guava的令牌桶算法:放入100個令牌 */ private static final RateLimiter RATE_LIMITER = RateLimiter.create(100); @Override public String filterType() { return PRE_TYPE; } @Override public int filterOrder() { return SERVLET_DETECTION_FILTER_ORDER - 1; } @Override public boolean shouldFilter() { return true; } @Override public Object run() { /** * tryAcquire(1, 0, TimeUnit.MICROSECONDS); * 取到一個令牌 */ if (!RATE_LIMITER.tryAcquire()) { throw new RateLimitException(); } return null; } }
參考資料: 微信點餐系統 springcloud學習筆記 dependencies與dependencyManagement的區別 springcloud(一)-集成Eureka 服務註冊與發現 Springcloud(二)-拆分微服務 SpringCloud(三)-應用間通訊 springcloud(四)-zuul網關與config統一配置 Springcloud-hystrix斷路器實現