此篇內容較長。
一、關於微服務當單體應用越來越大的時候,一個小的改動就要重新編譯打包部署(空間和時間都會增大),在這個過程中,服務是完全不能對外提供任何服務的,而微服務僅更新需要更新的服務,對其他服務的訪問是不會受影響的。
這時我們要將單體應用按業務進行拆分,這就是微服務,同是微服務可以部署在不同的伺服器中,同一個微服務可以複製N份,這就是分散式,因此微服務是分散式的基礎。
兩個微服務之間進行通訊,一般有兩種協議,HTTP協議和RPC協議,HTTP協議是長連結的,網路消耗上比RPC要多,不過HTTP是無狀態的,不同的微服務可以採用不同的開發語言,只要對外提供的是REST風格就能進行相互通訊。
springcloud基於HTTP協議,而dubbo基於RPC協議。
springcloud的基礎是springboot,因此每一個微服務都可以看成是一個微小的單體應用。
將應用拆分成單體應用後,構成微服務提供者和微服務消費者,然後使用框架中的輔助性功能,服務和發現註冊中心、負載均衡、斷路器、閘道器、配置中心。
二、微服務工程的構建1、新建maven父工程pom檔案
pom檔案
@Data@Accessors(chain = true) //實體類可以鏈式呼叫@NoArgsConstructor //無參構造@AllArgsConstructor //全參構造public class Person implements Serializable { private int id; private String name; private int age; @JsonIgnore //密碼序列化被忽略 private String password; //增加一個構造 public Person(String name, int age) { this.name = name; this.age = age; }}
釋出此公共模組,clean------install,當看到成功後
然後觀察父工程的pom檔案,會多出一個modules節點,同樣,以後每建立一個微服務,其微服務的artifactId名稱都會出現在父工程的modules節點中。
<modules> <module>microservice-cloud-api</module></modules>
3、新建微服務提供者8001
pom檔案
#服務提供商品server.port=8001 #資料庫連線配置spring.datasource.username=rootspring.datasource.password=XXXXspring.datasource.url=jdbc:mysql://localhost:3306/test
當前專案結構,資料庫框架採用的是Mybatis-Plus,配置好了直接採用程式碼化的sql拼接
PersonController類
@RestController@RequestMapping("/user")public class PersonController { @Autowired private PersonService personService; @GetMapping("/list") public List<Person> list() { return personService.getList(); } @GetMapping("/{id}") public Person getById(@PathVariable int id){ return personService.getPersonById(id); } @GetMapping("/{name}/{age}") public int insert(@PathVariable String name,@PathVariable String age){ return personService.insert(name,age); }}
4、新建微服務消費者80pom檔案
<?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"> <parent> <artifactId>microservice-cloud</artifactId> <groupId>icu.woodlum.cn</groupId> <version>0.0.1-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>microservice-cloud-consummer</artifactId> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>icu.woodlum.cn</groupId> <artifactId>microservice-cloud-api</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency> </dependencies></project>
application.properties
#設定埠server.port=80
當前專案結構
配置RestTemplate----------MyConfig.class
@Configurationpublic class MyConfig { @Bean public RestTemplate getRestTemplate(){ return new RestTemplate(); }}
ConsummerController控制類,透過restTemplate.getForObject方法,遠端呼叫8001埠服務中的控制器方法。
@RestControllerpublic class ConsummerController { //設定RestTemplate呼叫地址字首 private static final String REST_URL_PRE = "http://127.0.0.1:8001"; private RestTemplate restTemplate; public ConsummerController(RestTemplate restTemplate) { this.restTemplate = restTemplate; } @GetMapping("/list") public List<Person> list() { return restTemplate.getForObject(REST_URL_PRE + "/user/list", List.class); } @GetMapping("/{id}") public Person getById(@PathVariable int id){ return restTemplate.getForObject(REST_URL_PRE + "/user/{id}", Person.class,id); } @GetMapping("/add/{name}/{age}") public int insert(@PathVariable("name") String name,@PathVariable("age") String age){ Person person = new Person(name, Integer.parseInt(age)); return restTemplate.getForObject(REST_URL_PRE + "/user/{name}/{age}", Integer.class,person.getName(),person.getAge()); }}
依次啟動提供者和消費者服務
測試成功
三、服務和發現註冊中心(Eureka)透過上面的兩個微服務(提供者和消費者)可以發現,消費者透過IP:埠來呼叫消費者的控制器,耦合性太強,如果提供者的IP或埠發生變化,又或分散式提供多個提供者,這時直接呼叫IP字首的方法就不可用,因此我們需要一箇中間伺服器,透過中間伺服器來配置,消費者就不需要關心提供者的具體位置資訊,透過提供者的名稱自動獲取所在的伺服器位置,然後來進行呼叫。
這個就像網際網路中的域名解析伺服器,比如百度網站,其伺服器可能是多個,而且在不同的位置就會有多個IP,但是我們訪問的時候並不需要關心,我們只需要在瀏覽器位址列中輸入www.baidu.com即可,瀏覽器會訪問IP解析伺服器,從中選擇一個最優的伺服器進行訪問,服務和發現註冊中心就相當於這個IP域名解析伺服器,根據微服務名稱和規則來合理選擇在服務中心註冊的微服務。
Eureka現在劃歸於netflix專案下了,從其maven座標可以看出,之前的在mvn網站上已經標記為過時。
1、新建Eureka分散式服務註冊中心7001和7002pom檔案
為了本地測試多服務註冊中心,我們需要在Hosts檔案中新增:
127.0.0.1 www.woodlum7001.icu
127.0.0.1 www.woodlum7002.icu
server.port=7001#server.port=7002 #註冊中心主機名eureka.instance.hostname=www.woodlum7001.icu#eureka.instance.hostname=www.woodlum7002.icu #不註冊自身eureka.client.register-with-eureka=false#不檢索自身eureka.client.fetch-registry=false #單註冊中心#eureka.client.service-url.defaultZone=http://${eureka.instance.hostname}:${server.port}/eureka/#分散式註冊中心需要互相註冊eureka.client.service-url.defaultZone=http://www.woodlum7002.icu:7002/eureka/#eureka.client.service-url.defaultZone=http://www.woodlum7001.icu:7001/eureka/ #關閉自我保護,預設為true,超過85%服務掉線的時候,註冊中心不會移除掉線的例項#以保證服務的可用性,AP原則,服務可用性和容錯性,關閉在開發階段中使用,生產中建議使用預設#Renews/Renews threshold 預設0.85,小於此則會進行保護模式,註冊的客戶端不會刪除eureka.server.enable-self-preservation=false
在主程式中啟用Eureka伺服器
@SpringBootApplication//啟用Eureka伺服器,同理EurekaService7002@EnableEurekaServerpublic class EurekaService7001 { public static void main(String[] args) { SpringApplication.run(EurekaService7001.class,args); }}
2、改進服務提供者8001
maven中增座標
重點:分散式多副本提供微服務,spring.application.name此名稱完全一致,因為消費者是按微服務名稱來查詢的
#服務提供商品server.port=8001#Eureka註冊地址,多eurika註冊中心,地址用,隔開eureka.client.service-url.defaultZone=http://www.woodlum7001.icu:7001/eureka/,http://www.woodlum.icu7002:7002/eureka/#重點:客戶端例項名稱,Eureka中會顯示,且顯示為全大寫,# 分散式多副本提供微服務,此名稱完全一致,因為消費者是按微服務名稱來查詢的spring.application.name=mircroservice-cloud-provider #Status中自定義服務名稱資訊eureka.instance.instance-id=microservice-cloud-provider:8001#訪問路徑可以顯示IP地址,瀏覽器左下角顯示Ip而非localhosteureka.instance.prefer-ip-address=true #http://192.168.0.100:8001/actuator/info頁面資訊設定info.provider.name = microservice-cloud-provider-8001info.company.name = icu.woodlum.cn#獲取pom中節點資訊,注意,如果父pom中build中定界符設定為$,要使用@才能正常讀取#可能是新版本問題,當前工程actuator為2.3.4info.build.artifactId = @[email protected] = @project.modelVersion@ #資料庫連線spring.datasource.username=rootspring.datasource.password=XXXXspring.datasource.url=jdbc:mysql://localhost:3306/test
修改啟動類
@SpringBootApplication//啟動Eureka客戶端@EnableEurekaClientpublic class Provider8001 { public static void main(String[] args) { SpringApplication.run(Provider8001.class,args); }}
分別啟動服務中心7001、7002和服務提供8001
訪問http://www.woodlum7001.icu:7001/和http://www.woodlum7002.icu:7002/
訪問instance-id連結
3、啟用Discovery服務發現元件修改服務提供者的控制器
@RestController@RequestMapping("/user")public class PersonController { // 服務發現 @Autowired private DiscoveryClient client; @GetMapping("/discovery") public Object discovery(){ // 獲取服務中心的所有微服務名稱 List<String> list = client.getServices(); list.forEach(e -> { System.out.println(e); //以微服務名稱例項化微服務 List<ServiceInstance> instances = client.getInstances(e); //獲取微服務例項的屬性 instances.forEach(l -> { System.out.println(l.getInstanceId() + "|" + l.getHost() + "|" + l.getPort() + "|" + l.getUri() + "|" + l.getScheme() + "|" + l.getMetadata()); }); }); return this.client; } }
啟動器中啟用服務發現
@SpringBootApplication@EnableEurekaClient//啟用服務發現@EnableDiscoveryClientpublic class Provider8001 { public static void main(String[] args) { SpringApplication.run(Provider8001.class,args); }}
訪問服務發現服務
控制檯輸入:
mircroservice-cloud-providermicroservice-cloud-provider:8001|192.168.0.100|8001|http://192.168.0.100:8001|http|{management.port=8001, jmx.port=55787}
同理建立微服務提供者8002
四、Ribbon負載均衡當服務提供者有多個時,消費者如何去選擇相應的微服務?負載均衡就是處理消費者對於微服務請求的分配。
Ribbon整合在Eureka中,因此我們不需要再引入相關maven座標。
Ribbon是客戶端的負載均衡,因此我們只需要改進消費服務。
1、Ribbon負載均衡的使用application.properties檔案
server.port=80#消費方不註冊自己,但是會獲取服務eureka.client.register-with-eureka=false#預設獲取服務eureka.client.fetch-registry=true#eureka服務地址eureka.client.service-url.defaultZone=http://www.woodlum7001.icu:7001/eureka/,http://www.woodlum.icu7002:7002/eureka/
配置負載均衡@LoadBalanced開啟,預設為輪詢方式
@Configurationpublic class MyConfig { @Bean // 開啟LB (負載均衡) @LoadBalanced public RestTemplate getRestTemplate(){ return new RestTemplate(); }}
修改消費者控制器
@RestControllerpublic class ConsummerController { // ribbon負載均衡下,訪問的是微服務名稱 private static final String REST_URL_PRE = "http://MIRCROSERVICE-CLOUD-PROVIDER"; //設定Rest訪問地址 //private static final String REST_URL_PRE = "http://127.0.0.1:8001"; private RestTemplate restTemplate; public ConsummerController(RestTemplate restTemplate) { this.restTemplate = restTemplate; } @GetMapping("/list") public List<Person> list() { return restTemplate.getForObject(REST_URL_PRE + "/user/list", List.class); } @GetMapping("/{id}") public Person getById(@PathVariable int id){ return restTemplate.getForObject(REST_URL_PRE + "/user/{id}", Person.class,id); } @GetMapping("/add/{name}/{age}") public int insert(@PathVariable("name") String name,@PathVariable("age") String age){ Person person = new Person(name, Integer.parseInt(age)); return restTemplate.getForObject(REST_URL_PRE + "/user/{name}/{age}", Integer.class,person.getName(),person.getAge()); } @GetMapping("/discovery") public Object discovery(){ return restTemplate.getForObject(REST_URL_PRE + "/user/discovery",Object.class); }}
修改啟動類
@SpringBootApplication//啟用Eureka客戶端@EnableEurekaClientpublic class Consummer { public static void main(String[] args) { SpringApplication.run(Consummer.class, args); }}
啟動專案
測試
2、Ribbon規則演算法:預設為輪詢方式,多個服務提供者按順序會依次訪問
Ribbon規則有個介面IRule
RoundRobinRule
輪詢,依次呼叫服務提供者,預設的方式
RandomRule——隨機,多服務提供者隨機呼叫AvailabilityFilteringRule——先過濾掉由於故障處於斷路器跳閘狀態的服務,還有併發連線超過閾值的服務,然後對剩餘的服務列表進行輪詢WeightedResponseTimeRule——根據平均響應時間計算所有伺服器的權重響應時間越快,權重越大,剛啟動統計資訊不足會採用輪詢,當統計資訊足夠,會切換到WeightedResponseTimeRuleRetryRule——按照輪詢演算法獲取伺服器列表,若獲取服務失敗,在指定的時間重試,獲取可用的服務BestAvailableRule——先過濾由於多次訪問故障而處於斷路器跳閘狀態的伺服器,然後選擇併發量最小的服務ZoneAvoidanceRule——預設規則,複合判斷server所在區域的效能和server可用性選擇伺服器,一般為RoundRobinRule框架提供了這七種演算法,以下改用隨機演算法
@Configurationpublic class MyConfig { @Bean // 開啟LB (負載均衡) @LoadBalanced public RestTemplate getRestTemplate(){ return new RestTemplate(); } @Bean // 修改負載演算法策略為隨機演算法 public IRule getIRule(){ return new RandomRule(); }}
現在測試會發現,服務提供者的呼叫是隨機的,而非輪詢。
3、Ribbon自定義規則演算法:規則:每個服務呼叫五次後輪詢
新建rules包和其兩個類,注意,MyRibbonRule類不能放在主啟動類所在的包及其子包下,不能被@componentScan所掃描 ,否則,此規則會適配所有的Ribbon客戶端,達不到特殊化要求
自定義規則FiveRoundRibbonRule.class,複製一份框架上提供的IRule例項類的原始碼進行修改,重點是其中的choose方法。
public class FiveRoundRibbonRule extends AbstractLoadBalancerRule { private AtomicInteger nextServerCyclicCounter; private static final boolean AVAILABLE_ONLY_SERVERS = true; private static final boolean ALL_SERVERS = false; private static Logger log = LoggerFactory.getLogger(RoundRobinRule.class); private static int count = 0; //被呼叫次數 private static int index = 0; //當前微服務索引 public FiveRoundRibbonRule() { this.nextServerCyclicCounter = new AtomicInteger(0); } public FiveRoundRibbonRule(ILoadBalancer lb) { this(); this.setLoadBalancer(lb); } public Server choose(ILoadBalancer lb, Object key) { if (lb == null) { log.warn("no load balancer"); return null; } Server server = null; while (server == null){ if(Thread.interrupted()) { return null; } //獲取可用服務列表 List<Server> upList = lb.getReachableServers(); //獲取所有服務列表 List<Server> allList = lb.getAllServers(); //所有微服務數量 int serverCount = allList.size(); if ( serverCount == 0) { return null; } if (count < 5) { //呼叫次數小於5的時候,繼續呼叫當前服務 server = upList.get(index); count++; } else { //呼叫次數大於5的時候,次數歸零,服務索引加1 count = 0; index ++ ; //當前服務索引達到最大時歸零 if (index >= upList.size() ) { index = 0; } } if(server == null) { Thread.yield(); continue; } } return server; } public Server choose(Object key) { return this.choose(this.getLoadBalancer(), key); } public void initWithNiwsConfig(IClientConfig clientConfig) { }}
自定義規則的配置,MyRibbonRule.class
// 自定義Ribbon規則配置// 注意,此類不能放在主啟動類所在的包及其子包下,不能被@componentScan所掃描// 否則,此規則會適配所有的Ribbon客戶端,達不到特殊化要求@Configurationpublic class MyRibbonRule { @Bean // 修改負載演算法策略為隨機 public IRule getIRule(){ return new FiveRoundRibbonRule(); //return new RandomRule(); //此處也可以使用框架中的規則 }}
在啟動類中開啟自定義規則
@Configurationpublic class MyConfig { @Bean // 開啟LB (負載均衡) @LoadBalanced public RestTemplate getRestTemplate(){ return new RestTemplate(); } @Bean // 修改負載演算法策略為隨機演算法 public IRule getIRule(){ return new RandomRule(); }}
現在測試會發現,8001和8002服務會在呼叫五次後輪詢。
五、Fegin負載均衡透過上面的Ribbon客戶端負載均衡,我們是是透過RestTemplate來呼叫微服務的,如果restTemplate.getForObject(REST_URL_PRE + "/user/{id}", Person.class,id);這句會呼叫多次,我們每次都需要重複寫出,這樣做達不到設計模式所需要的開閉原則。
我們可以將對微服務提供者的呼叫邏輯提取出來,形成介面的方式,消費端呼叫的時候注入這個介面例項,再進行方法的呼叫。面向介面程式設計才是正道。
由於此介面可能會被多個消費服務呼叫,因此我們將它放到公共服務模組中。
使用Fegin負載均衡。
1、microservice-cloud-api公共模組中增fegin的maven座標
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> <version>2.2.6.RELEASE</version></dependency>
2、api公共模組中增PersonClientService介面
//fegin客戶端,value為被呼叫微服務的名稱@FeignClient(value = "MIRCROSERVICE-CLOUD-PROVIDER")public interface PersonClientService { @GetMapping("user/list") public List<Person> list(); @GetMapping("user/{id}") public Person getById(@PathVariable int id); @GetMapping("user/{name}/{age}") public int insert(@PathVariable String name,@PathVariable int age); @GetMapping("user/discovery") public Object discovery();}
3、maven---clean---install
4、新建microservice-cloud-consummer-feign消費模組
5、pom中不需要再引入fegin座標,因為此pom中已經引入了api模組,而api中又引入了fegin座標
6、application.properties
server.port=81 #消費方不註冊自己,但是會獲取服務eureka.client.register-with-eureka=false #預設獲取服務eureka.client.fetch-registry=true #eureka服務地址eureka.client.service-url.defaultZone=http://www.woodlum7001.icu:7001/eureka/,http://www.woodlum.icu7002:7002/eureka/
7、控制類改寫
@RestControllerpublic class ConsummerController { //像平常那樣注入PersonClientService介面的例項,透過介面來呼叫微服務 @Autowired private PersonClientService personClientService; @GetMapping("/list") public List<Person> list() { return personClientService.list(); } @GetMapping("/{id}") public Person getById(@PathVariable int id){ return personClientService.getById(id); } @GetMapping("/add/{name}/{age}") public int insert(@PathVariable("name") String name,@PathVariable("age") String age){ Person person = new Person(name, Integer.parseInt(age)); return personClientService.insert(person.getName(),person.getAge()); } @GetMapping("/discovery") public Object discovery(){ return personClientService.discovery(); }}
8、啟動類修改
啟用feign,basePackages指定api模組中@FeignClient註解所在的當前包,此處踩坑一天
@SpringBootApplication@EnableEurekaClient//啟用feign,basePackages指定api模組中@FeignClient註解所在的當前包,此處踩坑一天@EnableFeignClients(basePackages = {"icu.woodlum.cn.service"})public class ConsummerFeign { public static void main(String[] args) { SpringApplication.run(ConsummerFeign.class, args); }}
9、依次啟動微服務並測試
另外,服務提供的呼叫預設為輪詢方式
六、服務熔斷和服務降級Hystrix在分散式系統中,當一個微服務發生異常時,或者因此網路原因很大機率會發生一個微服務不可用時,如果一個微服務此時有大量請求進來,就會發生請求阻塞,微服務中的呼叫異常可能會導致整個系統崩潰或者系統資源耗盡,這時我們就需要對異常或者不可用的服務進行隔離,以阻止對其他應用的影響。
1、服務熔斷服務熔斷針對的是當前服務發生異常時,如何快速失敗,將異常的服務隔離開並快速返回一個失敗的訊息,防止異常的蔓延。
新建microservice-cloud-provider-8003-hystrix微服務
pom檔案增加Hystrix座標
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> <version>2.2.6.RELEASE</version></dependency>
修改控制器類和服務例項
@GetMapping("/{id}")// 當發生異常時,呼叫fallbackMethod方法@HystrixCommand(fallbackMethod = "getHystrixCommand")public Person getById(@PathVariable int id){ return personService.getPersonById(id);}//Hystrix熔斷時呼叫的方法public Person getHystrixCommand(@PathVariable int id) { return new Person().setId(id).setName("id:" + id + "不存在");} //PersonServiceImpl類中丟擲一個異常,如果不存在當前查詢id的資料時異常public Person getPersonById(int id) { Person person = personDao.selectById(id); if (person == null) { throw new RuntimeException("id:" + id + "不存在於資料庫中"); } return person;}
啟用Hystrix
#fegin客戶端啟用hystrixfeign.hystrix.enabled=true
測試,id100時資料庫中不存在,程式碼丟擲異常,服務熔斷,快速熔斷並呼叫設定好的方法。
2、服務降級服務降級針對的是當前服務不可用時,如何快速返回訊息,而不是一直等待。
服務降級一般使用除了網路故障導致的服務不可能用外,另外還有當某個微服務實時需求系統資源較多,這時我們就可以將不重要的少用的微服務停掉,讓更多的資源去處理緊急服務,這時一個請求如果在停掉的服務上時我們如何快速去響應訊息。
在服務熔斷中,熔斷方法耦合在控制器中,耦合性太強,由於控制器中呼叫了PersonClientService介面,因此我們可以在這個介面上來操作。
1、在api公共模組中新建服務失敗工廠類
public class FiveRoundRibbonRule extends AbstractLoadBalancerRule { private AtomicInteger nextServerCyclicCounter; private static final boolean AVAILABLE_ONLY_SERVERS = true; private static final boolean ALL_SERVERS = false; private static Logger log = LoggerFactory.getLogger(RoundRobinRule.class); private static int count = 0; //被呼叫次數 private static int index = 0; //當前微服務索引 public FiveRoundRibbonRule() { this.nextServerCyclicCounter = new AtomicInteger(0); } public FiveRoundRibbonRule(ILoadBalancer lb) { this(); this.setLoadBalancer(lb); } public Server choose(ILoadBalancer lb, Object key) { if (lb == null) { log.warn("no load balancer"); return null; } Server server = null; while (server == null){ if(Thread.interrupted()) { return null; } //獲取可用服務列表 List<Server> upList = lb.getReachableServers(); //獲取所有服務列表 List<Server> allList = lb.getAllServers(); //所有微服務數量 int serverCount = allList.size(); if ( serverCount == 0) { return null; } if (count < 5) { //呼叫次數小於5的時候,繼續呼叫當前服務 server = upList.get(index); count++; } else { //呼叫次數大於5的時候,次數歸零,服務索引加1 count = 0; index ++ ; //當前服務索引達到最大時歸零 if (index >= upList.size() ) { index = 0; } } if(server == null) { Thread.yield(); continue; } } return server; } public Server choose(Object key) { return this.choose(this.getLoadBalancer(), key); } public void initWithNiwsConfig(IClientConfig clientConfig) { }}
2、修改PersonClientService介面
// 自定義Ribbon規則配置// 注意,此類不能放在主啟動類所在的包及其子包下,不能被@componentScan所掃描// 否則,此規則會適配所有的Ribbon客戶端,達不到特殊化要求@Configurationpublic class MyRibbonRule { @Bean // 修改負載演算法策略為隨機 public IRule getIRule(){ return new FiveRoundRibbonRule(); //return new RandomRule(); //此處也可以使用框架中的規則 }}
3、測試--服務熔斷
4、關閉Hystrix8003微服務,此時再進行測試,此時也就是我們所說的服務降級
5、當服務熔斷和服務降級模式都啟動的時候,會先進行服務熔斷嘗試,如果服務不可用才會進行服務降級,因此上面的例子在服務正常時會先進行@HystrixCommand註解處的異常方法判斷。
3、DashBoard服務監控DashBoard(9001)針對的是Hystrix服務(8003)的監控,因此Hystrix服務提供者(8003)需要在方法上新增@HystrixCommand註解來啟動異常的服務熔斷。
1、建立DashBoard服務監控微服務microservice-cloud-consummer-hystrixdashboard9001
pom檔案
@SpringBootApplication@EnableEurekaClient// 自定義Ribbon規則,針對某一個微服務使用自定義規則@RibbonClient(name = "MIRCROSERVICE-CLOUD-PROVIDER",configuration = MyRibbonRule.class)public class Consummer { public static void main(String[] args) { SpringApplication.run(Consummer.class, args); }}
application.properties檔案
server.port=9001#需要設定監控目標允許的主機名稱,多主機名以,分隔hystrix.dashboard.proxy-stream-allow-list = 127.0.0.1,localhost
2、改寫Hystrix8003服務提供者
@GetMapping("/{id}")// 當發生異常時,呼叫fallbackMethod方法@HystrixCommand(fallbackMethod = "getHystrixCommand")public Person getById(@PathVariable int id){ return personService.getPersonById(id);} //Hystrix熔斷時呼叫的方法 public Person getHystrixCommand(@PathVariable int id) { return new Person().setId(id).setName("id:" + id + "資料庫中不存在");}
pom檔案中需要有actuator座標
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> <version>2.3.5.RELEASE</version></dependency>
需要將/hystrix.stream暴露出來
依次啟動Eurekia、Hystrix8003、HystrixBoard9001服務。
http ://localhost:9001/hystrix,如果看到下面介面,表示監控服務啟動成功。
http ://localhost:8003/hystrix.stream,被監控微服務的ping,先要呼叫一下http ://localhost:8003/user/1帶@HystricCommand註解的介面,不然ping會一直空白
將http ://localhost:8003/hystrix.stream填入http ://localhost:9001/hystrix網頁中。
一直重新整理監控api的呼叫
當監控api呼叫發生錯誤時
監控儀表盤指標,左邊實心圓的大小代表的是流量,顏色代表的是被監控微服務的健康度,綠色>黃色>橙色>紅色。
七、zuul閘道器服務閘道器主要是路由功能和過濾,和微服務系統構成一個反向代理,對外隱藏微服務的真實名稱及地址,同時方便外部訪問微服務,而不需要知道微服務的名稱及地址。
1、新建microservice-cloud-zuul-9000模組2、pom檔案首先,zuul要向Eureka註冊並獲取微服務,因此Eureka依賴必須;然後zuul的座標,zuul中包含了web,actuator、hystrix、ribbon等依賴
<?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"> <parent> <artifactId>microservice-cloud</artifactId> <groupId>icu.woodlum.cn</groupId> <version>0.0.1-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>microservice-cloud-zuul-9000</artifactId> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> <version>2.2.6.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-zuul</artifactId> <version>2.2.6.RELEASE</version> </dependency> </dependencies> </project>
3、application.properties檔案server.port= 9000 eureka.client.service-url.defaultZone= http://www.woodlum7001.icu:7001/eureka/,http://www.woodlum.icu7002:7002/eureka/eureka.client.instance.instance-id= zuulgataway.9000eureka.client.instance.prefer-ip-address= true spring.application.name= zuulgataway9000 #http://192.168.0.100:9000/actuator/info頁面資訊設定info.provider.name = microservice-cloud-zuul-9000info.company.name = icu.woodlum.cn#獲取pom中節點資訊,注意,父pom中build中雖然定界符設定為$,但是要使用@才能正常讀取#可能是新版本問題,當前工程actuator為2.3.4info.build.artifactId = @[email protected] = @project.modelVersion@
4、啟動類
@SpringBootApplication//開啟zuul代理@EnableZuulProxypublic class ZuulProxy9000 { public static void main(String[] args) { SpringApplication.run(ZuulProxy9000.class, args); }}
5、測試分別啟動Eureka叢集和服務提供者,再啟動zuul服務
但是其中被呼叫微服務的名稱在url中,這樣做不到隱藏
6、改進不對外暴露微服務的名稱application.properties中增加
#微服務對映路徑修改,之前為http://localhost:9000/mircroservice-cloud-provider/**#對映後為http://localhost:9000/provider1/**,#目的是在API中對外不暴露微服務名稱#provider1設定分組,可以針對多個微服務分別修改對映路徑 zuul.routes.provider1.serviceId= mircroservice-cloud-providerzuul.routes.provider1.path= /provider1/** #zuul.routes.provider2.serviceId= mircroservice-cloud-provider2#zuul.routes.provider2.path= /provider12/**
但是之前透過微服務名稱來訪問url還是可以訪問的,這時我們需要透過忽略微服務的名稱來控制不暴露
application.properties中增加
#忽略對映之前的原路徑,單個用具體微服務名稱,多個用,分隔,或者*全部zuul.ignored-services= mircroservice-cloud-provider
這時http ://localhost:9000/mircroservice-cloud-provider/user/list就不能訪問了
擴充知識點:設定統一公共字首
#設定統一公共字首zuul.prefix = /woodlum
八、config配置中心
當微服務專案越來越多的時候,每一個微服務都有自己的配置檔案,在微服務執行期間如果要修改配置內容,需要一個一個修改,然後重新打包執行,這樣會很麻煩。
因此就有了配置中心,將配置提取出來使用一個外部的集中配置來作為配置檔案,程式執行中自動從配置中心獲取配置檔案進行載入,實現了配置檔案的動態化。
springcloud配置中心分為服務端和客戶端。
1、配置中心服務端springcloud配置檔案預設採用git儲存方式,服務端是一個獨立的微服務,主要使用是連線客戶端和配置檔案所在伺服器的一箇中轉服務。
a、github建立配置倉庫b、新增config配置微服務3344pom檔案
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-config-server</artifactId> <version>2.2.6.RELEASE</version></dependency>
啟動類
@SpringBootApplication//開啟配置伺服器@EnableConfigServerpublic class ConfigServer3344 { public static void main(String[] args) { SpringApplication.run(ConfigServer3344.class,args); }}
application.properties
server.port= 3344#當前微服務名稱spring.application.name= microservicecloud-configserver#git倉庫地址spring.cloud.config.server.git.uri= https://github.com/woodlum2017/spring-cloud-config-demo
2、改進Eureka註冊微服務7003、服務提供者8004和服務消費者82
建立三個properties屬性檔案,儲存為utf-8格式,其中內容為原本各自的屬性配置,將其push到github
三個微服務的application.properties檔案刪除,各自新建bootstrap.properties檔案,要spring中,bootstrap屬於系統級的配置,而application屬於使用者級的配置,系統級配置會優先載入
#eureka的bootstrapt.properties #需要從github上讀取的資源名稱,注意沒有properties字尾名spring.cloud.config.name=springcloud-config-eureka-config#讀取的分支名spring.cloud.config.label=master#本微服務啟動後先去找3344號服務,透過SpringCloudConfig獲取Gitlub的服務地址spring.cloud.config.uri=http://www.woodlum3344.icu:3344 #provider的bootstrapt.properties #需要從github上讀取的資源名稱,注意沒有properties字尾名spring.cloud.config.name=springcloud-config-provider-config#讀取的分支名spring.cloud.config.label=master#本微服務啟動後先去找3344號服務,透過SpringCloudConfig獲取Gitlub的服務地址spring.cloud.config.uri=http://www.woodlum3344.icu:3344 #comsummer的bootstrapt.properties #需要從github上讀取的資源名稱,注意沒有properties字尾名spring.cloud.config.name=springcloud-config-consummer#讀取的分支名spring.cloud.config.label=master#本微服務啟動後先去找3344號服務,透過SpringCloudConfig獲取Gitlub的服務地址spring.cloud.config.uri=http://www.woodlum3344.icu:3344
啟動微服務
測試