public abstract class LoadBalance<T> {
protected List<T> data;
protected int size;
public LoadBalance(T[] data) {
this.data = Arrays.asList(data);
this.size = data.length;
}
public abstract T get();
public int size() {
return size;
}
public synchronized void add(T t) {
data.add(t);
size = data.size();
}
}
轮询:
public class RoundRobinBalance<T> extends LoadBalance<T> {
private int pos = 0;
public RoundRobinBalance(T[] data) {
super(data);
}
@Override
public synchronized T get() {
T result = data.get(pos);
this.pos++;
if (pos >= size) {
pos = 0;
}
return result;
}
}
获取服务:
public RpcConnect getConnect(String serviceName) {
try {
return loadBalanceMap.get(serviceName).get();
} catch (NullPointerException e) {
throw new ProxyException(e);
}
}
引入 Consul 后的结构:
RpcServer -> Consul
:Server 在 Consul 中注册自己,成为 Consul 中的 Service。Consul -> RpcServer
:Consul 使用 check 对 Server 的健康状况进行检查。RpcClient -> Consul
:Client 连接 Consul,读取 Client 所需的服务,并订阅服务。Consul -> RpcClient
:向启用订阅的 Client 发送当前健康的 Service 列表。RpcServer <--> RpcClient
(旧):Client 发起 RPC 请求,Server 响应。此处应有结构图(待补充)
引入 Consul 流程
启动 Consul
docker
启动单节点server
模式consul
源自博客-p 8500:8500
:映射容器端口到宿主机端口-d
:后台运行容器,并输出容器 IDconsul
:docker 镜像名称。PS:注意不要使用progrium/consul
镜像。agnet -server
:设置server
模式-ui
:启动 UI 模式,可通过访问127.0.0.1:8500
查看consul
运行情况-bootstrap-expect=1
:期望的服务数量,consul 服务将等待直到连接的服务达到期望值。这里是由于本地开发测试使用,因此使用了单节点(1)。-client
:将绑定到client
接口的地址,可以是HTTP、DNS、RPC服务器(?),默认值127.0.0.1
。猜测类似于MySQL
等,如果127.0.0.1
则仅接受本机上的请求,而0.0.0.0
则不限。宿主机端口访问问题
Consul 提供了很多检查服务健康的方式(Register Check API、Checks),包含
http
、ttl
、tcp
、grpc
等方式。由于我的 RPC 实现使用是基于 TCP 的,因此使用的是tcp check
。这就面临一个问题,docker
容器(consul
)需要访问宿主机(server
)端口,来检查 Server 是否运行良好。想要访问宿主机端口并不是直接通过
127.0.0.1
即可,这里宿主机与容器之间似乎是建立了一个内网的关系,它们同在一个内网中,只能通过内网 IP 来互相访问。幸好
docker
提供了解决方案:MAC OS
:通过 hostdocker0
即可访问宿主机。(未测试)MAC OS
:docker.for.mac.localhost
。PS:需要 docker 版本 17.06 以上 参考资料。工程中使用 Consul
可选择的 Java 客户端:
这里我选择的是 consul-client,API 的调用命名和调用方式较为舒服。
RpcServer:注册 Consul 服务
:warning:注意:上述代码中存在缺陷尚未解决。由于涉及容器访问宿主机端口问题,因此 consul 中 check 的 host 使用的是
docker.for.mac.localhost
,实际应使用变量rpcHost
。RpcClient:获取服务列表 & 订阅服务
虽然向 consul 订阅服务之后,consul 会推送一次服务信息。但我在初始化服务信息时,还是手动获取了一遍服务列表。
healthCacheFunction:
LoadBalance.java:
轮询:
获取服务: