一 准备工作
搭建Eureka服务器
服务提供者
服务调用者
二 新建项目spring-lb-server,充当Eureka服务器
1 启动spring-lb-server
三 新建项目spring-lb-provider
1 新建启动类
package org.crazyit.cloud;
import java.util.Scanner;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableEurekaClient
public class ProviderApp {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
String port = scan.nextLine();
new SpringApplicationBuilder(ProviderApp.class).properties("server.port=" + port).run(args);
}
}
2 新建实体类
package org.crazyit.cloud;
public class Police {
private Integer id;
private String name;
private String message;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
3 新建控制类
package org.crazyit.cloud;
import javax.servlet.http.HttpServletRequest;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class PoliceController {
@RequestMapping(value = "/call/{id}", method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
public Police call(@PathVariable Integer id, HttpServletRequest request) {
Police p = new Police();
p.setId(id);
p.setName("angus");
p.setMessage(request.getRequestURL().toString());
return p;
}
}
4 启动8080和8081两个实例
四 新建项目spring-lb-invoker
1 新建一个启动类
package org.crazyit.cloud;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableEurekaClient
public class InvokerApp {
public static void main(String[] args) {
new SpringApplicationBuilder(InvokerApp.class).web(true).run(args);
}
}
2 新建控制类
package org.crazyit.cloud;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import com.netflix.loadbalancer.ZoneAwareLoadBalancer;
@RestController
@Configuration
public class TestController {
@Bean
@LoadBalanced
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
@GetMapping("/router")
@ResponseBody
public String router() {
RestTemplate tpl = getRestTemplate();
String json = tpl.getForObject("http://spring-lb-provider/call/1", String.class);
return json;
}
}
3 启动服务并测试
浏览器反复输入
http://localhost:9000/router
在下面两个结果轮询
{"id":1,"name":"angus","message":"http://DESKTOP-5SDKDG4:8080/call/1"}
{"id":1,"name":"angus","message":"http://DESKTOP-5SDKDG4:8081/call/1"}
说明实现了轮询。
4 新建自定义规则,让大部分选择8080端口
package org.crazyit.cloud;
import java.util.List;
import java.util.Random;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.Server;
public class MyRule implements IRule {
private ILoadBalancer lb;
public Server choose(Object key) {
System.out.println("这是自定义的规则类");
Random r = new Random();
int randomNum = r.nextInt(10);
List<Server> servers = lb.getAllServers();
if(randomNum > 7) {
Server s = getServerByPort(servers, 8081);
return s;
}
return getServerByPort(servers, 8080);
}
private Server getServerByPort(List<Server> servers, int port) {
for(Server s : servers) {
if(s.getPort() == port) {
return s;
}
}
return null;
}
public void setLoadBalancer(ILoadBalancer lb) {
this.lb = lb;
}
public ILoadBalancer getLoadBalancer() {
return lb;
}
}
5 新建配置类
package org.crazyit.cloud;
import org.springframework.context.annotation.Bean;
import com.netflix.loadbalancer.IRule;
public class MyConfig {
@Bean
public IRule getRule() {
return new MyRule();
}
}
6 新建MyClient类,和配置类关联
package org.crazyit.cloud;
importorg.springframework.cloud.netflix.ribbon.RibbonClient;
@RibbonClient(name = "spring-lb-provider", configuration = MyConfig.class)
public class MyClient {
}
说明:上面代码的name之所以为spring-lb-provider,是因为要和getForObject中的spring-lb-provider保持一致,否则无法关联到MyClient类,更不会和MyConfig配置发生关联
@GetMapping("/router")
@ResponseBody
public String router() {
RestTemplate tpl = getRestTemplate();
String json = tpl.getForObject("http://spring-lb-provider/call/1", String.class);
return json;
}
7 测试自定义的规则
输出结果大部分如下:
{"id":1,"name":"angus","message":"http://DESKTOP-5SDKDG4:8080/call/1"}
再看控制台输出:
这是自定义的规则类
这是自定义的规则类
这是自定义的规则类
这是自定义的规则类
这是自定义的规则类
这是自定义的规则类
这是自定义的规则类
这是自定义的规则类
说明自定义规则生效。
8 通过配置文件中的NFLoadBalancerRuleClassName进行配置,去掉@RibbonClient(name = "spring-lb-provider", configuration = MyConfig.class)注解,在配置文件中加入下面一段,也能起到同样的效果
spring-lb-provider:
ribbon:
NFLoadBalancerRuleClassName: org.crazyit.cloud.MyRule
9 再次测试自定义规则,效果一样
输出结果大部分如下:
{"id":1,"name":"angus","message":"http://DESKTOP-5SDKDG4:8080/call/1"}
再看控制台输出:
这是自定义的规则类
这是自定义的规则类
这是自定义的规则类
这是自定义的规则类
这是自定义的规则类
这是自定义的规则类
这是自定义的规则类
这是自定义的规则类
10 LoadBalancerClient的应用
在控制类中加入以下代码
@Autowired
private LoadBalancerClient client;
@RequestMapping(value = "/lb", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public ServiceInstance lb() {
ServiceInstance si = client.choose("spring-lb-provider");
return si;
}
11 测试
大部分输出下面内容
{"serviceId":"spring-lb-provider","server":{"host":"DESKTOP-5SDKDG4","port":8080,"id":"DESKTOP-5SDKDG4:8080","zone":"defaultZone","readyToServe":true,"instanceInfo":{"instanceId":"DESKTOP-5SDKDG4:spring-lb-provider:8080","app":"SPRING-LB-PROVIDER","appGroupName":null,"ipAddr":"192.168.0.106","sid":"na","homePageUrl":"http://DESKTOP-5SDKDG4:8080/","statusPageUrl":"http://DESKTOP-5SDKDG4:8080/info","healthCheckUrl":"http://DESKTOP-5SDKDG4:8080/health","secureHealthCheckUrl":null,"vipAddress":"spring-lb-provider","secureVipAddress":"spring-lb-provider","countryId":1,"dataCenterInfo":{"@class":"com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo","name":"MyOwn"},"hostName":"DESKTOP-5SDKDG4","status":"UP","leaseInfo":{"renewalIntervalInSecs":30,"durationInSecs":90,"registrationTimestamp":1531641405889,"lastRenewalTimestamp":1531643144502,"evictionTimestamp":0,"serviceUpTimestamp":1531641403608},"isCoordinatingDiscoveryServer":false,"metadata":{},"lastUpdatedTimestamp":1531641405889,"lastDirtyTimestamp":1531641341331,"actionType":"ADDED","asgName":null,"overriddenStatus":"UNKNOWN"},"metaInfo":{"serverGroup":null,"instanceId":"DESKTOP-5SDKDG4:spring-lb-provider:8080","appName":"SPRING-LB-PROVIDER","serviceIdForDiscovery":"spring-lb-provider"},"alive":true,"hostPort":"DESKTOP-5SDKDG4:8080"},"secure":false,"metadata":{},"host":"DESKTOP-5SDKDG4","port":8080,"uri":"http://DESKTOP-5SDKDG4:8080"}
再观察控制台:
这是自定义的规则类
这是自定义的规则类
这是自定义的规则类
这是自定义的规则类
这是自定义的规则类
这是自定义的规则类
这是自定义的规则类
这是自定义的规则类
这是自定义的规则类
这是自定义的规则类
这是自定义的规则类
说明当访问微服务spring-lb-provider时,我们的配置生效了。
12 输出客户端的配置
在控制类中加入以下代码
@Autowired
private SpringClientFactory factory;
@RequestMapping(value = "/fa", method = RequestMethod.GET)
public String factory() {
ZoneAwareLoadBalancer lb = (ZoneAwareLoadBalancer)factory.getLoadBalancer("default");
System.out.println(lb.getRule().getClass().getName());
ZoneAwareLoadBalancer lb2 = (ZoneAwareLoadBalancer)factory.getLoadBalancer("spring-lb-provider");
System.out.println(lb2.getRule().getClass().getName());
return "";
}
13 测试
输出
com.netflix.loadbalancer.ZoneAvoidanceRule
org.crazyit.cloud.MyRule
再看看配置文件
spring-lb-provider:
ribbon:
NFLoadBalancerRuleClassName: org.crazyit.cloud.MyRule
当选择default时,采用的规则是com.netflix.loadbalancer.ZoneAvoidanceRule
当选择spring-lb-provider时,采用的规则是org.crazyit.cloud.MyRule