先來思考一個問題
通過上一章的操作,我們已經可以實現微服務之間的呼叫。但是我們把服務提供者的網路地址(ip,埠)等寫死到了程式碼中,這種做法存在許多問題:
一旦服務提供者地址變化,就需要手工修改程式碼
一旦是多個服務提供者,無法實現負載均衡功能
一旦服務變得越來越多,人工維護呼叫關係困難
那麼應該怎麼解決呢, 這時候就需要通過註冊中心動態的實現服務治理。
什麼是服務治理
服務治理是微服務架構中最核心最基本的模組。用於實現各個微服務的自動化註冊與發現
服務註冊: 在服務治理框架中,都會構建一個註冊中心,每個服務單元向註冊中心登記自己提供服
務的詳細資訊。並在註冊中心形成一張服務的清單,服務註冊中心需要以心跳的方式去監測清單中
的服務是否可用,如果不可用,需要在服務清單中剔除不可用的服務。
服務發現: 服務呼叫方向服務註冊中心諮詢服務,並獲取所有服務的範例清單,實現對具體服務範例的存取。
服務註冊中心,它是微服務架構非常重要的一個元件,在微服務架構裡主要起到了協調者的一個作用。註冊中心一般包含如下幾個功能:
服務註冊:儲存服務提供者和服務呼叫者的資訊
服務訂閱:服務呼叫者訂閱服務提供者的資訊,註冊中心向訂閱者推播提供者的資訊
設定訂閱:服務提供者和服務呼叫者訂閱微服務相關的設定
設定下發:主動將設定推播給服務提供者和服務呼叫者
檢測服務提供者的健康情況,如果發現異常,執行服務剔除
Zookeeper
zookeeper是一個分佈式服務架構,是Apache Hadoop 的一個子專案,它主要是用來解決分佈式
應用中經常遇到的一些數據管理問題,如:統一命名服務、狀態同步服務、叢集管理、分佈式應用
設定項的管理等。
Eureka
Eureka是Springcloud Netflix中的重要元件,主要作用就是做服務註冊和發現。但是現在已經閉
源
Consul
Consul是基於GO語言開發的開源工具,主要面向分佈式,服務化的系統提供服務註冊、服務發現
和設定管理的功能。Consul的功能都很實用,其中包括:服務註冊/發現、健康檢查、Key/Value
儲存、多數據中心和分佈式一致性保證等特性。Consul本身只是一個二進制的可執行檔案,所以
安裝和部署都非常簡單,只需要從官網下載後,在執行對應的啓動指令碼即可。
Nacos
Nacos是一個更易於構建雲原生應用的動態服務發現、設定管理和服務管理平臺。它是 Spring
Cloud Alibaba 元件之一,負責服務註冊發現和服務設定,可以這樣認爲nacos=eureka+config。
Nacos 致力於幫助您發現、設定和管理微服務。Nacos 提供了一組簡單易用的特性集,幫助您快速
實現動態服務發現、服務設定、服務元數據及流量管理。
從上面的介紹就可以看出,nacos的作用就是一個註冊中心,用來管理註冊上來的各個微服務。
下載Nacos:https://nacos.io/en-us/
win下啓動:\nacos\bin目錄下點選startup.cmd啓動
存取:http://127.0.0.1:8848/nacos/ 預設賬號密碼均爲:nacos
接下來開始修改 shop-product 模組的程式碼, 將其註冊到nacos服務上
1、在pom.xml中新增nacos的依賴
<!--nacos用戶端-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
yml
server:
port: 8081
spring:
application:
name: user
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://192.168.0.2:3306/demo?characterEncoding=utf-8&useSSL=false&serverTimezone=UTC
username: root
password: 123456
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
logging:
level:
com.jx.user.dao : debug
mybatis:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
Application
package com.jx.user;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
/**
* @author yangxinlei
* @date 2020/8/10
*/
@SpringBootApplication
@EnableDiscoveryClient
public class UserApplication {
public static void main(String[] args) {
SpringApplication.run(UserApplication.class,args);
}
}
order服務同樣方式註冊到註冊中心
修改JxOrderServiceImpl
package com.jx.user.service.impl;
import com.alibaba.fastjson.JSON;
import com.jx.common.User;
import com.jx.user.dao.JxOrderMapper;
import com.jx.user.service.JxOrderService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* @author yangxinlei
* @date 2020/8/11
*/
@Service
@Slf4j
public class JxOrderServiceImpl implements JxOrderService {
@Autowired
private RestTemplate restTemplate;
@Autowired
private DiscoveryClient discoveryClient;
@Autowired
private JxOrderMapper jxOrderMapper;
@Override
public Object getOrderList() throws Exception {
log.info(">>使用者資訊");
//從nacos中獲取服務地址
ServiceInstance serviceInstance = discoveryClient.getInstances("user").get(0);
String url = serviceInstance.getHost() + ":" + serviceInstance.getPort();
log.info(">>從nacos中獲取到的微服務地址爲:" + url);
//通過restTemplate呼叫商品微服務
List<User> product = restTemplate.getForObject("http://user/user/getUserList", List.class);
log.info(">>使用者資訊,查詢結果:" + JSON.toJSONString(product));
return product;
}
}
JxOrderApplication
package com.jx.user;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
/**
* @author yangxinlei
* @date 2020/8/11
*/
@SpringBootApplication
@EnableDiscoveryClient
public class JxOrderApplication {
public static void main(String[] args) {
SpringApplication.run(JxOrderApplication.class,args);
}
@Bean
@LoadBalanced
RestTemplate restTemplate(){
return new RestTemplate();
}
}
通俗的講, 負載均衡就是將負載(工作任務,存取請求)進行分攤到多個操作單元(伺服器,元件)上進行執行。
根據負載均衡發生位置的不同,一般分爲伺服器端負載均衡 和用戶端負載均衡 。
伺服器端負載均衡指的是發生在服務提供者一方,比如常見的nginx負載均衡
而用戶端負載均衡指的是發生在服務請求的一方,也就是在發送請求之前已經選好了由哪個範例處理請求。
通過idea再啓動一個 user 微服務,設定其埠爲8083
-Dserver.port=8083
通過nacos檢視微服務的啓動情況
修改JxOrderServiceImpl
package com.jx.user.service.impl;
import com.alibaba.fastjson.JSON;
import com.jx.common.User;
import com.jx.user.dao.JxOrderMapper;
import com.jx.user.service.JxOrderService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Random;
/**
* @author yangxinlei
* @date 2020/8/11
*/
@Service
@Slf4j
public class JxOrderServiceImpl implements JxOrderService {
@Autowired
private RestTemplate restTemplate;
@Autowired
private DiscoveryClient discoveryClient;
@Autowired
private JxOrderMapper jxOrderMapper;
@Override
public Object getOrderList() throws Exception {
log.info(">>使用者資訊");
//從nacos中獲取服務地址
List<ServiceInstance> instances = discoveryClient.getInstances("user");
int index = new Random().nextInt(instances.size());
ServiceInstance serviceInstance = instances.get(index);
String url = serviceInstance.getHost() + ":" + serviceInstance.getPort();
log.info(">>從nacos中獲取到的微服務地址爲:" + url);
//通過restTemplate呼叫商品微服務
List<User> product = restTemplate.getForObject("http://user/user/getUserList", List.class);
log.info(">>使用者資訊,查詢結果:" + JSON.toJSONString(product));
return product;
}
}
Ribbon是Spring Cloud的一個元件, 它可以讓我們使用一個註解就能輕鬆的搞定負載均衡
在RestTemplate 的生成方法上新增@LoadBalanced註解
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
修改服務呼叫
package com.jx.user.service.impl;
import com.alibaba.fastjson.JSON;
import com.jx.common.User;
import com.jx.user.dao.JxOrderMapper;
import com.jx.user.service.JxOrderService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Random;
/**
* @author yangxinlei
* @date 2020/8/11
*/
@Service
@Slf4j
public class JxOrderServiceImpl implements JxOrderService {
@Autowired
private RestTemplate restTemplate;
@Override
public Object getOrderList() throws Exception {
log.info(">>使用者資訊");
//直接使用微服務名字, 從nacos中獲取服務地址
String url = "user";
//通過restTemplate呼叫商品微服務
List<User> product = restTemplate.getForObject("http://"+url+"/user/getUserList", List.class);
log.info(">>使用者資訊,查詢結果:" + JSON.toJSONString(product));
return product;
}
}
策略名 | 策略描述 | 實現說明 |
---|---|---|
BestAvailableRule | 選擇一個最小的併發 請求的server | 逐個考察Server,如果Server被tripped了,則忽略,在選擇其中ActiveRequestsCount最小的server |
AvailabilityFilteringRule | 過濾掉那些因爲一直連線失敗的被標記爲circuit tripped的後端server,並過濾掉那些高併發的的後端server(activeconnections 超過設定的閾值) | 使用一個AvailabilityPredicate來包含過濾server的邏輯,其實就就是檢查status裡記錄的各個server的執行狀態 |
WeightedResponseTimeRule | 根據相應時間分配一個weight,相應時間越長,weight越小,被選中的可能性越低。 | 一個後臺執行緒定期的從status裏面讀取評價響應時間,爲每個server計算一個weight。Weight的計算也比較簡單responsetime 減去每個server自己平均的responsetime是server的權重。當剛開始執行,沒有形成statas時,使用roubine策略選擇server。 |
RetryRule | 對選定的負載均衡策略機上重試機制 機製。 | 在一個設定時間段內當選擇server不成功,則一直嘗試使用subRule的方式選擇一個可用的server |
RoundRobinRule | 輪詢方式輪詢選擇server | 輪詢index,選擇index對應位置的server |
RandomRule | 隨機選擇一個server | 在index上隨機,選擇index對應位置的server |
ZoneAvoidanceRule | 複合判斷server所在區域的效能和server的可用性選擇server | 使用ZoneAvoidancePredicate和AvailabilityPredicate來判斷是否選擇某個server,前一個判斷判定一個zone的執行效能是否可用,剔除不可用的zone(的所有server),AvailabilityPredicate用於過濾掉連線數過多的Server。 |
修改application.yml
user: # 呼叫的提供者的名稱
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
加入Fegin的依賴
<!--fegin元件-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
JxOrderApplication
package com.jx.user;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
/**
* @author yangxinlei
* @date 2020/8/11
*/
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients//開啓Fegin
public class JxOrderApplication {
public static void main(String[] args) {
SpringApplication.run(JxOrderApplication.class,args);
}
@Bean
@LoadBalanced
RestTemplate restTemplate(){
return new RestTemplate();
}
}
新建一個service
package com.jx.user.service;
import com.jx.common.User;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import java.util.List;
/**
* @author yangxinlei
* @date 2020/8/12
*/
@FeignClient("user")
public interface UserService {
@GetMapping(value = "/user/getUserList")
Object userGetUserList();
}
呼叫
@Autowired
private UserService userService;
Object product = userService.userGetUserList();
log.info(">>使用者資訊,查詢結果:" + product);