背景:開發,測試環境都使用了一個註冊中心,所以在開發,測試環境不想進行負載均衡,原因1:自己本地調試方便,原因2:不想測試環境負載到自己的機器上(代碼不一致)
註冊中心使用nacos
實現原理:使用ThreadLocal保存本機唯一信息,在進行負載時,從多個服務取相關信息和本機信息進行對比,如果一致就使用該服務進行調用,也就確保了所有請求都是在同一個機器上進行
1:feign層定製負載均衡
//ThreadLocal
public class RuleThreadLocal {
private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>(){
@Override
protected String initialValue() {
return "";
}
};
public static void setRule(String type){
contextHolder.set(type);
}
public static String getRule(){
return contextHolder.get();
}
/**
* 清除上下文數據
*/
public static void clearRule() {
contextHolder.remove();
}
}
// 自定義負載策略
@Slf4j
public class MyRibbonRule extends AbstractLoadBalancerRule {
@Override
public void initWithNiwsConfig(IClientConfig iClientConfig) {
}
@Override
public Server choose(Object key) {
Server server = null;
try {
ILoadBalancer lb = getLoadBalancer();
int count = 0;
while (server == null && count++ < 10) {
List<Server> reachableServers = lb.getReachableServers();
List<Server> allServers = lb.getAllServers();
int upCount = reachableServers.size();
int serverCount = allServers.size();
if ((upCount == 0) || (serverCount == 0)) {
log.warn("No up servers available from load balancer: " + lb);
return null;
}
for (Server server1 : reachableServers) {
NacosServer nacosServer = (NacosServer) server1;
Map<String, String> metadata = nacosServer.getMetadata();
// 獲取服務器標識信息
String hostname = metadata.get("hostname");
String rule = getRule();
if (rule.equals(hostname)) {
if (server1.isAlive() && (server1.isReadyToServe())) {
return server1;
}
}
}
if (server == null) {
/* Transient. */
Thread.yield();
continue;
}
if (server.isAlive() && (server.isReadyToServe())) {
return (server);
}
// Next.
server = null;
}
if (count >= 10) {
log.warn("No available alive servers after 10 tries from load balancer: "
+ lb);
}
} catch (Exception e) {
log.error("rule error", e);
throw new CommonException("rule error");
} finally {
RuleThreadLocal.clearRule();
}
return server;
}
private String getRule() {
return RuleThreadLocal.getRule();
}
}
// Ribbon配置文件,使用我們自己的負載策略
@Configuration
public class RibbonConfig {
@Profile({"dev", "test"})
@Bean
public IRule ribbonRule() {
return new MyRibbonRule();
}
}
// 創建一個feign的請求攔截器
@AllArgsConstructor
public class RuleRequestInterceptor implements RequestInterceptor {
String rule;
@Override
public void apply(RequestTemplate template) {
// 在創建請求之前設置本機服務器信息
RuleThreadLocal.setRule(rule);
}
}
// 配置自定義攔截器
@Profile({"dev","test"})
@Bean
public RequestInterceptor requestInterceptor() {
return new RuleRequestInterceptor(rule);
}
bootstrap.yml 配置
spring:
cloud:
nacos:
config:
file-extension: properties
server-addr: xxxx
namespace: xxxx
discovery:
server-addr: xxxxx
namespace: xxxxx
metadata:
hostname: ${USER:root} #指定服務器名稱,每個機器上應該是不一樣的
// RibbonClients配置,xxxx爲你自己的服務
@RibbonClients({
@RibbonClient(value = "xxxx", configuration = RibbonConfig.class),
@RibbonClient(value = "xxxx", configuration = RibbonConfig.class)
})
public class GateWayServerApplication {
public static void main(String[] args) {
SpringApplication.run(GateWayServerApplication.class, args);
}
}