原理图
大致思路:假设服务端是一个由10台机器构成的集群节点,每台机器在启动发布服务时,都会将各自的ip地址及端口号往zookeeper中注册。之后客户端会从zookeeper中拿到所有该服务注册的节点地址,并根据负载均衡算法,选取其中一个服务进行远程调用。
核心代码-服务端
注册中心
/**
* 1.zookeeper连接
* 2.注册服务
*/
public class RegisterCenterImpl implements IRegisterCenter {
private CuratorFramework curatorFramework;
//连接zookeeper
{
curatorFramework= CuratorFrameworkFactory.builder().
connectString(com.ft.rmi.zk.ZkConfig.CONNECTION_STR).
sessionTimeoutMs(4000).
retryPolicy(new ExponentialBackoffRetry(1000,10)).build();
curatorFramework.start();
}
public void register(String serviceName, String serviceAddress) {
//注册相应的服务
try {
//例:servicePath=/registrys/com.ft.rmi.TaofutHelloWorld
String servicePath= ZkConfig.ZK_REGISTER_PATH + "/" +serviceName;
//如果节点不存在,则创建
if(curatorFramework.checkExists().forPath(servicePath)==null){
curatorFramework.create().creatingParentsIfNeeded().
withMode(CreateMode.PERSISTENT).forPath(servicePath,"0".getBytes());
}
//例:addressPath=/registrys/com.ft.rmi.TaofutHelloWorld/127.0.0.1:8080
String addressPath=servicePath+"/"+serviceAddress;
//创建临时节点:/registrys/com.ft.rmi.TaofutHelloWorld/127.0.0.1:8080
String rsNode=curatorFramework.create().withMode(CreateMode.EPHEMERAL).
forPath(addressPath,"0".getBytes());
System.out.println("服务注册成功:"+rsNode);
} catch (Exception e) {
e.printStackTrace();
}
}
}
服务发布
/**
* 用来对外发布一个服务,并且监听客户端发来的请求
*/
public class RpcServer {
//定义一个线程池
private static final ExecutorService executorService= Executors.newCachedThreadPool();
private IRegisterCenter registerCenter;//注册中心
private String serviceAddress;//服务发布地址
//存放服务名称和服务对象之间的关系
Map<String,Object> handlerMap=new HashMap();
public RpcServer(IRegisterCenter registerCenter, String serviceAddress) {
this.registerCenter = registerCenter;
this.serviceAddress = serviceAddress;
}
/**
* 绑定服务名称和服务对象
* @param services
*/
public void bind(Object... services){
for(Object service:services){
RpcAnnotation annotation=service.getClass().getAnnotation(RpcAnnotation.class);
//例:serviceName=com.ft.rmi.TaofutHelloWorld
String serviceName=annotation.value().getName();
String version=annotation.version();
if(version!=null&&!version.equals("")){
serviceName=serviceName+"-"+version;
}
//例:com.ft.rmi.TaofutHelloWorld,实例
handlerMap.put(serviceName,service);//绑定服务接口名称对应的服务
}
}
/**
* 发布服务
*/
public void publisher(){
ServerSocket serverSocket=null;
try {
String[] addrs=serviceAddress.split(":");
serverSocket=new ServerSocket(Integer.parseInt(addrs[1]));//启动一个服务监听
for(String interfaceName:handlerMap.keySet()){
registerCenter.register(interfaceName,serviceAddress);
System.out.println("服务注册成功:"+interfaceName+"->"+serviceAddress);
}
while (true){
Socket socket=serverSocket.accept();
//开启线程去处理客户端发来的请求
executorService.execute(new ProcessorHandler(socket,handlerMap));
}
} catch (IOException e) {
e.printStackTrace();
}finally {
if(serverSocket!=null){
try {
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
处理客户端请求
/**
* 处理客户端发来的请求
*/
public class ProcessorHandler implements Runnable {
private Socket socket;
Map<String,Object> handlerMap;
public ProcessorHandler(Socket socket, Map<String,Object> handlerMap) {
this.socket = socket;
this.handlerMap = handlerMap;
}
public void run() {
//处理socket请求,服务端接收数据,从输入流里读
ObjectInputStream objectInputStream=null;
ObjectOutputStream outputStream=null;
try {
objectInputStream=new ObjectInputStream(socket.getInputStream());
RpcRequest rpcRequest=(RpcRequest) objectInputStream.readObject();
Object result=invoke(rpcRequest);
//将服务端执行的结果 通过socket回传给客户端
outputStream=new ObjectOutputStream(socket.getOutputStream());
outputStream.writeObject(result);
outputStream.flush();
} catch (Exception e) {
e.printStackTrace();
}finally {
if(objectInputStream!=null){
try {
objectInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(outputStream!=null){
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 客户端:发来请求告诉服务端,我要调用你这个服务里面的xx方法,并将xx方法的详细信息传递给服务端。
* 服务端:收到请求及参数后,利用反射去执行了xx方法,并将执行的结果返回给客户端。
* @param rpcRequest
* @return
* @throws InvocationTargetException
* @throws IllegalAccessException
* @throws NoSuchMethodException
*/
private Object invoke(RpcRequest rpcRequest) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {
Object[] args=rpcRequest.getParameters();
Class<?>[] types=new Class[args.length];
for(int i=0;i<args.length;i++){
types[i]=args[i].getClass();
}
String serviceName=rpcRequest.getClassName();
String version=rpcRequest.getVersion();
if(version!=null&&!version.equals("")){
serviceName=serviceName+"-"+version;
}
//中handlerMap中,根据客户端请求的地址,去拿到响应的服务,通过反射发起调用
Object service=handlerMap.get(serviceName);
Method method=service.getClass().getMethod(rpcRequest.getMethodName(),types);
return method.invoke(service,args);
}
}
核心代码-客户端
服务发现
/**
* 1.zookeeper连接
* 2.服务发现,并且根据负载均衡算法选择一个服务节点
*/
public class ServiceDiscoveryImpl implements IServiceDiscovery {
private List<String> repos=new ArrayList();
private CuratorFramework curatorFramework;
private String address;
public ServiceDiscoveryImpl(String address) {
this.address = address;
curatorFramework= CuratorFrameworkFactory.builder().
connectString(address).
sessionTimeoutMs(4000).
retryPolicy(new ExponentialBackoffRetry(1000,10)).build();
curatorFramework.start();
}
public String discover(String serviceName) {
String path=ZkConfig.ZK_REGISTER_PATH+"/"+serviceName;
try{
repos=curatorFramework.getChildren().forPath(path);
}catch (Exception e){
throw new RuntimeException("获取子节点异常:"+e);
}
//动态发现服务节点的变化(监听)
registerWatcher(path);
//负载均衡机制-随机选择一个服务进行调用
LoadBalance loadBalance=new RandomLoadBalance();
return loadBalance.selectHost(repos);
}
private void registerWatcher(final String path){
PathChildrenCache childrenCache=new PathChildrenCache(curatorFramework,path,true);
PathChildrenCacheListener pathChildrenCacheListener=new PathChildrenCacheListener() {
public void childEvent(CuratorFramework curatorFramework, PathChildrenCacheEvent pathChildrenCacheEvent) throws Exception {
repos=curatorFramework.getChildren().forPath(path);
}
};
childrenCache.getListenable().addListener(pathChildrenCacheListener);
try {
childrenCache.start();
} catch (Exception e) {
throw new RuntimeException("注册PathChild Watcher异常:"+e);
}
}
}
发起远程调用
/**
* 传递调用参数,封装RpcRequest
*/
public class RemoteInvocationHandler implements InvocationHandler {
private IServiceDiscovery serviceDiscovery;
private String version;
public RemoteInvocationHandler(IServiceDiscovery serviceDiscovery,String version) {
this.serviceDiscovery = serviceDiscovery;
this.version=version;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
RpcRequest rpcRequest=new RpcRequest();
//例:className=com.ft.rmi.TaofutHelloWorld
rpcRequest.setClassName(method.getDeclaringClass().getName());
//例:methodName=sayHello
rpcRequest.setMethodName(method.getName());
//例:parameters=我是架构师
rpcRequest.setParameters(args);
rpcRequest.setVersion(version);
//通过接口名称拿到对应的服务地址
String serviceAddress=serviceDiscovery.discover(rpcRequest.getClassName());
//传输
TCPTransport tcpTransport=new TCPTransport(serviceAddress);
//向服务端发送请求,并且传输 rpcRequest
return tcpTransport.send(rpcRequest);
}
}
传输和接收过程
/**
* Socket编程
*/
public class TCPTransport {
private String serviceAddress;
public TCPTransport(String serviceAddress) {
this.serviceAddress=serviceAddress;
}
public Socket newSocket(){
System.out.println("创建一个新的连接");
Socket socket=null;
try {
//与服务端建立连接
String[] addrs=serviceAddress.split(":");
socket=new Socket(addrs[0],Integer.parseInt(addrs[1]));
return socket;
} catch (IOException e) {
throw new RuntimeException("连接建立失败");
}
}
/**
* 与服务端建立连接,并且向服务端传输 rpcRequest(服务端需要执行的方法详细信息)
* 收到服务端执行完方法的结果
* @param rpcRequest
* @return
*/
public Object send(RpcRequest rpcRequest){
Socket socket=null;
try {
socket=newSocket();
ObjectOutputStream outputStream=new ObjectOutputStream(socket.getOutputStream());
outputStream.writeObject(rpcRequest);//rpcRequest序列化传输
outputStream.flush();
ObjectInputStream inputStream=new ObjectInputStream(socket.getInputStream());
Object result=inputStream.readObject();
inputStream.close();
outputStream.close();
return result;
} catch (Exception e) {
throw new RuntimeException("发起远程调用异常",e);
} finally {
if(socket!=null){
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}