1,jar包依赖
junit
junit
4.11
test
org.apache.zookeeper
zookeeper
3.4.8
org.apache.curator
curator-framework
4.0.0
org.apache.curator
curator-client
4.0.0
org.apache.curator
curator-recipes
4.0.0
2,VO和服务地址
* VO
public class RPCRequest implements Serializable {
private static final long serialVersionUID = -406351179958827029L;
private String className;
private String methodName;
private Object[] parameters;
private String version;
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
public String getClassName() {
return className;
}
public void setClassName(String className) {
this.className = className;
}
public String getMethodName() {
return methodName;
}
public void setMethodName(String methodName) {
this.methodName = methodName;
}
public Object[] getParameters() {
return parameters;
}
public void setParameters(Object[] parameters) {
this.parameters = parameters;
}
}
* 服务地址
public class ZKConfig {
public static final String ZK_HOST = "192.168.91.128:2181,192.168.91.129:2181,192.168.91.130:2181";
public static final String ZK_REGISTER_PATH = "/registers";
}
* 自定义注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface RpcAnnotation {
/**
* 对外发布的服务接口地址
*/
Class> value();
/**
* 版本号
*/
String version() default "";
}
3,注册服务接口
* 通过构造方法初始化Curator
private CuratorFramework curatorFramework;
public RegisterService() {
curatorFramework = CuratorFrameworkFactory.builder()
.connectString(ZKConfig.ZK_HOST).sessionTimeoutMs(4000)
.retryPolicy(new ExponentialBackoffRetry(1000, 3)).build();
curatorFramework.start();
}
* 注册服务
-- 注册服务名称为根节点下的持久化节点
-- 注册服务对应的服务器地址为服务名称节点下的临时节点
-- 每次服务重启时会清空临时节点(服务的分布式服务器),并重新注册临时节点
public void register(String serviceName, String serviceAddress) {
try {
// 注册相应的服务 --> 不存在则创建
String serviceUrl = ZKConfig.ZK_REGISTER_PATH + "/" + serviceName;
if (null == curatorFramework.checkExists().forPath(serviceUrl)) {
curatorFramework.create().creatingParentsIfNeeded()
.withMode(CreateMode.PERSISTENT).forPath(serviceUrl, "0".getBytes());
}
// 获取地址的完整路径
String addressUrl = serviceUrl + "/" + serviceAddress;
// 注册地址的临时节点
String rsNode = curatorFramework.create().withMode(CreateMode.EPHEMERAL).forPath(addressUrl, "0".getBytes());
System.out.println("服务注册成功, rsNode : " + rsNode);
} catch (Exception e) {
e.printStackTrace();
}
}
4,绑定服务
* 通过构造初始化构造中心,服务发布地址已经服务绑定集合(Map)
public class RPCServer {
private final ExecutorService executorService = Executors.newCachedThreadPool();
/**
* 注册中心
*/
private IRegisterService registerService;
/**
* 服务发布地址
*/
private String serviceAddress;
Map handlerMap = new HashMap();
public RPCServer(IRegisterService registerService, String serviceAddress) {
this.registerService = registerService;
this.serviceAddress = serviceAddress;
}
* 对外服务接口
@RpcAnnotation(value = ISelfHelloService.class)
public class SelfHelloService implements ISelfHelloService {
@Override
public String sayHello(String msg) {
return "[I'M 8081]HELLO : " + msg;
}
}
* 根据注解信息获取服务名称,服务版本号并绑定服务
public void bind(Object ... services) {
for (Object service : services) {
RpcAnnotation annotation = service.getClass().getAnnotation(RpcAnnotation.class);
String serviceName = annotation.value().getName();
String version = annotation.version();
if (null != version && !"".equals(version)) {
serviceName = serviceName + "-" + version;
}
// 绑定服务接口名称对应的服务
handlerMap.put(serviceName, service);
}
}
5,发布服务端服务
* 根据服务地址启动服务,并注册服务到注册中心
public void publishServer() {
ServerSocket serverSocket = null;
try {
// 服务端启动服务代码内容==================
// 启动服务
serverSocket = new ServerSocket(Integer.valueOf(serviceAddress.split(":", -1)[1]));
for (String interfaceName : handlerMap.keySet()) {
//
registerService.register(interfaceName, serviceAddress);
System.out.println("注册服务成功, interfaceName : " + interfaceName + ", serviceAddress : " + serviceAddress);
}
// 服务端获取客户端请求代码处理======================
// 获取监听
while (true) {
Socket socket = serverSocket.accept();
executorService.execute(new ProcessorHandler(socket, handlerMap));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (null != serverSocket) {
try {
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
* main方法启动服务,通过启动两个服务实现模拟分布式,并验证负载均衡
public class ClusterServerService_1 {
public static void main(String[] args) {
ISelfHelloService helloService = new SelfHelloService();
IRegisterService registerService = new RegisterService();
// 初始化注册中心和服务端口信息
RPCServer server = new RPCServer(registerService, "127.0.0.1:8081");
// 绑定服务
server.bind(helloService);
// 发布并注册服务
server.publishServer();
System.out.println("服务发布成功");
}
}
public class ClusterServerService_2 {
public static void main(String[] args) {
ISelfHelloService helloService = new SelfHello2Service();
IRegisterService registerService = new RegisterService();
// 初始化注册中心和服务端口信息
RPCServer server = new RPCServer(registerService, "127.0.0.1:8082");
// 绑定服务
server.bind(helloService);
// 发布并注册服务
server.publishServer();
System.out.println("服务发布成功");
}
}
* 服务启动执行结果
* zookeeper查询节点
1,jar包依赖
junit
junit
4.11
test
com.gupao.server
RMIServerProject
1.0-SNAPSHOT
org.apache.zookeeper
zookeeper
3.4.8
org.apache.curator
curator-framework
4.0.0
org.apache.curator
curator-client
4.0.0
org.apache.curator
curator-recipes
4.0.0
2,通过Zookeeper获取注册服务
* 通过构造方法初始化Curator客户端
public class DiscoveryService implements IDiscoveryService {
private CuratorFramework curatorFramework;
List serviceNodes = new ArrayList();
private String address;
public DiscoveryService(String address){
curatorFramework = CuratorFrameworkFactory.builder()
.connectString(address).sessionTimeoutMs(4000)
.retryPolicy(new ExponentialBackoffRetry(1000, 3)).build();
curatorFramework.start();
}
* 通过serviceName获取服务节点
public String discovery(String serviceName) {
try {
// 根据路径获取所有节点
String serviceUrl = ZKConfig.ZK_REGISTER_PATH + "/" + serviceName;
serviceNodes = curatorFramework.getChildren().forPath(serviceUrl);
// 发起服务监听
registerWatcher(serviceUrl);
// 实现简易负载均衡
ILoadBanlanceService loadBanlanceService = new LoadBalanceService();
return loadBanlanceService.selectHost(serviceNodes);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
* 服务监听代码
private void registerWatcher(final String path) {
try {
PathChildrenCache pathChildrenCache = new PathChildrenCache(curatorFramework, path, true);
PathChildrenCacheListener pathChildrenCacheListener = new PathChildrenCacheListener() {
@Override
public void childEvent(CuratorFramework curatorFramework, PathChildrenCacheEvent pathChildrenCacheEvent) throws Exception {
serviceNodes = curatorFramework.getChildren().forPath(path);
}
};
pathChildrenCache.getListenable().addListener(pathChildrenCacheListener);
pathChildrenCache.start();
} catch (Exception e) {
e.printStackTrace();
}
}
* 通过随机数实现简易负载均衡代码
* 接口
public interface ILoadBanlanceService {
String selectHost(List serviceNodes);
}
* 抽象类构造模板方法
public abstract class AbstractBalanceService implements ILoadBanlanceService {
@Override
public String selectHost(List serviceNodes) {
if (null == serviceNodes || serviceNodes.size() == 0) {
return "";
}
if (serviceNodes.size() == 1) {
return serviceNodes.get(0);
}
return doSelect(serviceNodes);
}
public abstract String doSelect(List selectNodes);
}
* 实现类实现简易负载均衡
public class LoadBalanceService extends AbstractBalanceService {
@Override
public String doSelect(List selectNodes) {
Random random = new Random();
int indexSelect = random.nextInt(selectNodes.size());
return selectNodes.get(indexSelect);
}
}
3,构建动态代理,发起服务请求
* 构建动态代理对象
public class RPCClientProxy {
private IDiscoveryService discoveryService;
private String version;
public RPCClientProxy(IDiscoveryService discoveryService, String version) {
this.discoveryService = discoveryService;
this.version = version;
}
public T clientProxy(final Class interafaceCls) {
return (T) Proxy.newProxyInstance(interafaceCls.getClassLoader(),
new Class[]{interafaceCls}, new RemoteInvocationHandler(discoveryService, version));
}
}
5,InvocationHandler实现
* 通过构造方法初始化注册中心对象和版本号
private IDiscoveryService discoveryService;
private String version;
public RemoteInvocationHandler(IDiscoveryService discoveryService, String version) {
this.discoveryService = discoveryService;
this.version = version;
}
* 动态代理实现方法调用并提交服务请求发送
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
RPCRequest request = new RPCRequest();
request.setClassName(method.getDeclaringClass().getName());
request.setMethodName(method.getName());
request.setParameters(args);
request.setVersion(version);
String serviceName = request.getClassName();
if (null != version && !"".equals(version)) {
serviceName = serviceName + "-" + version;
}
String serviceAddress = discoveryService.discovery(serviceName);
RPCTransPort transformPort = new RPCTransPort(serviceAddress);
return transformPort.send(request);
}
4,发送服务请求到客户端
* 通过负载均衡获取的服务地址构建套接字
public class RPCTransPort {
private String serviceAddress;
public RPCTransPort(String serviceAddress) {
this.serviceAddress = serviceAddress;
}
private Socket newSocket() {
Socket socket = null;
try {
String[] addressArr = serviceAddress.split(":", -1);
socket = new Socket(addressArr[0], Integer.valueOf(addressArr[1]));
return socket;
} catch (Exception e) {
throw new RuntimeException("CREATED CONNECTION FAILED=======");
} finally {
}
}
* 通过套接字输出流构建对象输出流
* 通过对象输出流写出服务请求参数
public Object send(RPCRequest request) {
Socket socket = null;
try {
// 输出服务请求参数到服务端代码======================
socket = newSocket();
ObjectOutputStream outputStream = new ObjectOutputStream(socket.getOutputStream());
outputStream.writeObject(request);
outputStream.flush();
// 服务端响应结果代码=========================
ObjectInputStream inputStream = new ObjectInputStream(socket.getInputStream());
Object result = inputStream.readObject();
inputStream.close();
outputStream.close();
return result;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
5,客户端发送请求main方法
public class ClientController {
public static void main(String[] args) {
IDiscoveryService discoveryService = new DiscoveryService(ZKConfig.ZK_HOST);
for (int i = 0; i < 10; i++) {
RPCClientProxy rpcClientProxy = new RPCClientProxy(discoveryService, null);
// 构建代理对象
ISelfHelloService proxyService =
rpcClientProxy.clientProxy(ISelfHelloService.class);
// 调用服务方法
String result = proxyService.sayHello("ZPJ");
System.out.println(result);
}
}
}
1,通过服务监听获取客户端请求套接字 (class RPCServer)
public void publishServer() {
ServerSocket serverSocket = null;
try {
// 服务端启动服务代码内容==================
// 启动服务
serverSocket = new ServerSocket(Integer.valueOf(serviceAddress.split(":", -1)[1]));
for (String interfaceName : handlerMap.keySet()) {
//
registerService.register(interfaceName, serviceAddress);
System.out.println("注册服务成功, interfaceName : " + interfaceName + ", serviceAddress : " + serviceAddress);
}
// 服务端获取客户端请求代码处理======================
// 获取监听
while (true) {
Socket socket = serverSocket.accept();
executorService.execute(new ProcessorHandler(socket, handlerMap));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (null != serverSocket) {
try {
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
2,开启服务器线程处理套接字
public class RPCServer {
private final ExecutorService executorService = Executors.newCachedThreadPool();
while (true) {
Socket socket = serverSocket.accept();
executorService.execute(new ProcessorHandler(socket, handlerMap));
}
* 通过构造方法初始化套接字和服务绑定map
public class ProcessorHandler implements Runnable {
private Socket socket;
Map handlerMap;
public ProcessorHandler(Socket socket, Map handlerMap) {
this.socket = socket;
this.handlerMap = handlerMap;
}
* 获取套接字输入流构建对象输入流
* 通过输入流读取传输对象
* 解析对象参数, 执行请求方法并获取结果
public void run() {
// 处理请求
ObjectInputStream inputStream = null;
try {
// 服务端处理客户端请求代码======================
inputStream = new ObjectInputStream(socket.getInputStream());
RPCRequest request = (RPCRequest) inputStream.readObject();
Object result = invoke(request);
// 服务端返回执行结果到客户端代码========================
ObjectOutputStream outputStream = new ObjectOutputStream(socket.getOutputStream());
outputStream.writeObject(result);
outputStream.flush();
outputStream.close();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (null != inputStream) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 通过执行执行客户端请求方法
*/
public Object invoke(RPCRequest request) {
try {
Object[] parameters = request.getParameters();
Class>[] types = new Class[parameters.length];
for (int i = 0; i < parameters.length; i++) {
types[i] = parameters[i].getClass();
}
String version = request.getVersion();
String serviceName = request.getClassName();
if (null != version && !"".equals(version)) {
serviceName = serviceName + "-" + version;
}
Object service = handlerMap.get(serviceName);
Method method = service.getClass().getMethod(request.getMethodName(), types);
return method.invoke(service, parameters);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
* 根据套接字输出流构建对象输出流
* 通过输出流输出服务执行结果(代码片段)
// 服务端返回执行结果到客户端代码========================
ObjectOutputStream outputStream = new ObjectOutputStream(socket.getOutputStream());
outputStream.writeObject(result);
outputStream.flush();
outputStream.close();
1,根据套接字输入流构建对象输入流(class RPCTransPort)
public Object send(RPCRequest request) {
Socket socket = null;
try {
// 输出服务请求参数到服务端代码======================
socket = newSocket();
ObjectOutputStream outputStream = new ObjectOutputStream(socket.getOutputStream());
outputStream.writeObject(request);
outputStream.flush();
// 服务端响应结果代码=========================
ObjectInputStream inputStream = new ObjectInputStream(socket.getInputStream());
Object result = inputStream.readObject();
inputStream.close();
outputStream.close();
return result;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
2,根据对象输入流读取服务端返回执行结果
3,处理返回结果
// 服务端响应结果代码=========================
ObjectInputStream inputStream = new ObjectInputStream(socket.getInputStream());
Object result = inputStream.readObject();
inputStream.close();
outputStream.close();
return result;
4,请求结果
* 从下图可以看到,8081和8082端口各有出现;这是因为服务端在注册服务时模拟分布式注册多个服务节点,客户端在处理服务节 点时进行了简易的基于随机数的负载均衡,main方法中循环调用
* 请求main方法分十次进行服务获取,每一次都会随机获取8081或者8082端口服务,并进行调用
public class ClientController {
public static void main(String[] args) {
IDiscoveryService discoveryService = new DiscoveryService(ZKConfig.ZK_HOST);
for (int i = 0; i < 10; i++) {
RPCClientProxy rpcClientProxy = new RPCClientProxy(discoveryService, null);
// 构建代理对象
ISelfHelloService proxyService =
rpcClientProxy.clientProxy(ISelfHelloService.class);
// 调用服务方法
String result = proxyService.sayHello("ZPJ");
System.out.println(result);
}
}
}
1,服务端暴露服务自定义注解上添加version属性
@RpcAnnotation(value = ISelfHelloService.class, version = "1.0")
public class SelfHelloService implements ISelfHelloService {
@Override
public String sayHello(String msg) {
return "[I'M 8081]HELLO : " + msg;
}
}
@RpcAnnotation(value = ISelfHelloService.class, version = "2.0")
public class SelfHello2Service implements ISelfHelloService {
@Override
public String sayHello(String msg) {
return "[I'M 8082]HELLO : " + msg;
}
}
2,服务绑定根据版本号分别绑定两个服务并注册到注册中心
* 修改main方法
public class ServerService {
public static void main(String[] args) {
ISelfHelloService helloService = new SelfHelloService();
ISelfHelloService helloService2 = new SelfHello2Service();
IRegisterService registerService = new RegisterService();
// 初始化注册中心和服务端口信息
RPCServer server = new RPCServer(registerService, "127.0.0.1:8080");
// 绑定服务
server.bind(helloService, helloService2);
// 发布服务
server.publishServer();
System.out.println("服务发布成功");
}
}
* 注意服务绑定处注册的服务节点为服务名和版本号的组合
public void bind(Object ... services) {
for (Object service : services) {
RpcAnnotation annotation = service.getClass().getAnnotation(RpcAnnotation.class);
String serviceName = annotation.value().getName();
String version = annotation.version();
if (null != version && !"".equals(version)) {
serviceName = serviceName + "-" + version;
}
// 绑定服务接口名称对应的服务
handlerMap.put(serviceName, service);
}
}
3,客户端构建动态代理时,逐层传递要请求的版本号到服务发送端(服务层代码同上)
* RPCClientProxy第二个参数即为version参数
public class ClientController {
public static void main(String[] args) {
IDiscoveryService discoveryService = new DiscoveryService(ZKConfig.ZK_HOST);
for (int i = 1; i < 3; i++) {
RPCClientProxy rpcClientProxy = new RPCClientProxy(discoveryService, i + ".0");
// 构建代理对象
ISelfHelloService proxyService =
rpcClientProxy.clientProxy(ISelfHelloService.class);
// 调用服务方法
String result = proxyService.sayHello("ZPJ");
System.out.println(result);
}
}
}
* 再次附上RPCClientProxy代码
public class RPCClientProxy {
private IDiscoveryService discoveryService;
private String version;
public RPCClientProxy(IDiscoveryService discoveryService, String version) {
this.discoveryService = discoveryService;
this.version = version;
}
public T clientProxy(final Class interafaceCls) {
return (T) Proxy.newProxyInstance(interafaceCls.getClassLoader(),
new Class[]{interafaceCls}, new RemoteInvocationHandler(discoveryService, version));
}
}
4,服务端解析添加版本号处理(服务层代码同上)
* 重复代码, 为了直观
public Object invoke(RPCRequest request) {
try {
Object[] parameters = request.getParameters();
Class>[] types = new Class[parameters.length];
for (int i = 0; i < parameters.length; i++) {
types[i] = parameters[i].getClass();
}
String version = request.getVersion();
String serviceName = request.getClassName();
if (null != version && !"".equals(version)) {
serviceName = serviceName + "-" + version;
}
Object service = handlerMap.get(serviceName);
Method method = service.getClass().getMethod(request.getMethodName(), types);
return method.invoke(service, parameters);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
5,执行结果
* 服务端
* 客户端
* zookeeper节点