code.zip

一、思路

1.1、代码组件-ServerListUpdater

功能:获取到所有的服务列表

01.png

1.2、代码组件-ILoadBalancer

功能:提供客户端的负载均衡功能

02.png

1.3、组件-IRule

功能:提供具体的负载均衡策略

03.png

二、具体实现

项目结构如下:
00.png

2.1、ServerListUpdater 服务列表更新

默认实现: DefaultServerListUpdater
04.png

  1. /**
  2. * <p> 默认服务列表更新 </p>
  3. *
  4. * @Author 彳失口亍
  5. */
  6. public class DefaultServerListUpdater extends Thread implements ServerListUpdater {
  7. public DefaultServerListUpdater(ILoadBalancer loadBalancer) {
  8. this.loadBalancer = loadBalancer;
  9. }
  10. /** 负载均衡对象 **/
  11. private ILoadBalancer loadBalancer;
  12. /** 最后更新时间 **/
  13. private volatile String lastUpdate;
  14. /** 服务列表集合 **/
  15. private static List<Server> serverList = new ArrayList<>();
  16. @Override
  17. public void start(UpdateAction updateAction) {
  18. updateAction.doUpdate();
  19. lastUpdate = System.currentTimeMillis() + "";
  20. }
  21. @Override
  22. public void run() {
  23. while(true){
  24. this.start(()->{
  25. // update action
  26. synchronized (this){
  27. // 模拟从其他平台拉取到的服务数据
  28. List<Server> serversOnRepository = Arrays.asList(
  29. new Server("ribbon-server", "127.0.0.1", "9530"));
  30. // 更新服务列表 (增量更新-剔除 host 和 port 相同的新增数据)
  31. List<Server> addServers = new ArrayList<>();
  32. serversOnRepository.forEach(item ->{
  33. if(!serverList.contains(item)){
  34. addServers.add(item);
  35. }
  36. });
  37. if(!CollectionUtils.isEmpty(addServers)){
  38. serverList.addAll(addServers);
  39. loadBalancer.addServers(addServers);
  40. }
  41. }
  42. });
  43. try {
  44. TimeUnit.SECONDS.sleep(20);
  45. } catch (InterruptedException e) {
  46. break;
  47. }
  48. }
  49. }
  50. @Override
  51. public String getLastUpdate() {
  52. return lastUpdate;
  53. }
  54. }

主要方法逻辑在 run 方法中,定时从某个地址获取服务列表(这里空实现),然后将新增的服务信息保存起来。

2.2、ILoadBalancer 负载均衡功能实现类

默认实现:DefaultLoadBalancer
05.png

  1. /**
  2. * <p> 默认的负载均衡器 </p>
  3. *
  4. * @Author 彳失口亍
  5. */
  6. public class DefaultLoadBalancer implements ILoadBalancer{
  7. public DefaultLoadBalancer(IRule rule) {
  8. this.rule = rule;
  9. }
  10. /** 服务列表集合 key:serverName,value Server.class **/
  11. private static List<Server> serverList = new ArrayList<>();
  12. /** 服务获取算法 **/
  13. private IRule rule;
  14. @Override
  15. public void addServers(List<Server> newServers) {
  16. serverList.addAll(newServers);
  17. }
  18. @Override
  19. public Server chooseServer(Object key) {
  20. if(null == key){
  21. throw new RuntimeException("not found the server " + key);
  22. }
  23. // 获取到指定服务名称的服务列表
  24. List<Server> servers = serverList.stream()
  25. .filter(filter -> filter.getServerName().equals(key))
  26. .collect(Collectors.toList());
  27. if(StringUtils.isEmpty(servers)){
  28. throw new RuntimeException("not found the server " + key);
  29. }
  30. return rule.choose(servers);
  31. }
  32. @Override
  33. public List<Server> getAllServers() {
  34. return Collections.unmodifiableList(serverList);
  35. }
  36. }

DefaultLoadBalancer 维护了 服务列表 serverList 该服务列表是由 ServerListUpdater 更新后同步过来的。

具体的负载均衡策略委派给了 IRule 来完成。

2.3、IRule 负载均衡策略实现

默认实现:RandomRule
06.png

  1. /**
  2. * <p> 随机算法 </p>
  3. *
  4. * @Author 彳失口亍
  5. */
  6. public class RandomRule implements IRule{
  7. @Override
  8. public Server choose(List<Server> servers) {
  9. int serverIndex = new Random().nextInt(servers.size());
  10. return servers.get(serverIndex);
  11. }
  12. }

2.4、RestTemplate 请求过滤器

默认实现:LoadBalanceClientHttpRequestInterceptor

  1. /**
  2. * <p> 负载均衡过滤器 RestTemplate 过滤器</p>
  3. *
  4. * @Author 彳失口亍
  5. */
  6. public class LoadBalanceClientHttpRequestInterceptor implements ClientHttpRequestInterceptor {
  7. public LoadBalanceClientHttpRequestInterceptor(ILoadBalancer loadBalancer) {
  8. this.loadBalancer = loadBalancer;
  9. }
  10. private ILoadBalancer loadBalancer;
  11. @Override
  12. public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
  13. LoadBalanceHttpRequest loadBalanceHttpRequest = new LoadBalanceHttpRequest(request, loadBalancer);
  14. ClientHttpResponse originalResponse = execution.execute(loadBalanceHttpRequest, body);
  15. return originalResponse;
  16. }
  17. /** 请求包装类 **/
  18. public static class LoadBalanceHttpRequest extends HttpRequestWrapper{
  19. private ILoadBalancer loadBalancer;
  20. public LoadBalanceHttpRequest(HttpRequest request,ILoadBalancer loadBalancer) {
  21. super(request);
  22. this.loadBalancer = loadBalancer;
  23. }
  24. @Override
  25. public URI getURI() {
  26. URI originUri = super.getURI();
  27. URI newUri = null;
  28. // 如果能从服务列表中获取到服务信息,则进行 url 的重新拼接
  29. Server server = loadBalancer.chooseServer(originUri.getHost());
  30. if(null != server){
  31. StringBuffer sb = new StringBuffer();
  32. sb.append(server.getHost())
  33. .append(":")
  34. .append(Optional.ofNullable(server.getPort()).orElse("80"));
  35. String newHost = sb.toString();
  36. try{
  37. String url = originUri.toURL().toString();
  38. String newUrl = url.replaceFirst(originUri.getHost(), newHost);
  39. newUri = new URI(newUrl);
  40. }catch (URISyntaxException e){
  41. } catch (MalformedURLException e) {
  42. e.printStackTrace();
  43. }
  44. return newUri;
  45. }
  46. return super.getURI();
  47. }
  48. }
  49. }

三、测试

3.1、测试代码1

自己手动拼接 url

  1. /**
  2. * <p> 客户端 API 测试 </p>
  3. *
  4. * @Author 彳失口亍
  5. */
  6. @RestController
  7. public class ClientApi {
  8. @Autowired
  9. private ILoadBalancer loadBalancer;
  10. @RequestMapping("/ribbon-client/demo")
  11. public String demo(){
  12. Server server = loadBalancer.chooseServer("ribbon-server");
  13. StringBuffer sb = new StringBuffer();
  14. sb.append("http://")
  15. .append(server.getHost())
  16. .append(":")
  17. .append(server.getPort())
  18. .append("/ribbon-server/api");
  19. // url = http://127.0.0.1:9530/ribbon-server/api
  20. RestTemplate restTemplate = new RestTemplate();
  21. ResponseEntity<String> entity = restTemplate.getForEntity(sb.toString(), String.class);
  22. String result = entity.getBody();
  23. return result;
  24. }
  25. }

3.2、测试代码2

让 RestTemplate 帮我们拼接

  1. @Autowired
  2. private RestTemplate restTemplate;
  3. @RequestMapping("/ribbon-client/demo2")
  4. public String demo2(){
  5. String serverName = "ribbon-server";
  6. StringBuffer sb = new StringBuffer();
  7. sb.append("http://")
  8. .append(serverName)
  9. .append("/ribbon-server/api");
  10. // url = http://ribbon-server/ribbon-server/api
  11. ResponseEntity<String> entity = restTemplate.getForEntity(sb.toString(), String.class);
  12. String result = entity.getBody();
  13. return result;
  14. }

四、代码逻辑图

07.png