运用前边写的RPC demo,结合zookeeper实现服务注册与发现、负载均衡(轮询)

运用前边写的RPC demo,结合zookeeper实现服务注册与发现、负载均衡(轮询)

本例模拟了user,order,good三个服务,每个服务有2~3个地址,数据只存放在java容器中未和数据库交互,未使用任何成熟框架(后面会用spring cloud实现真实些的项目),配置未实现热加载,后续可能会写关于热加载的东西。。过程中尽量实现基于配置的自动化,避免写死一些东西。

本人电脑很辣鸡内存只有2G,虚拟机卡爆,只能在Windows下操作,且只有这一台电脑,只能用不同端口模拟不同的主机,所以ip都是本地,本例未用到分布式锁,因为用了也测不出效果,后续会基于zookeeper写个简单的。代码较多,只贴出重点。

  • 服务注册代码

public static void regServer(String serverAddressKey) {
//LoadConfig为读取文件的配置文件工具类
        String rigCenterAddress = LoadConfig.getConfigValue("server_rigister_center");
        String[] rigCenterPathArr = rigCenterAddress.split(":");
        if(rigCenterPathArr.length != 2
                || rigCenterPathArr[0] == null || rigCenterPathArr[1] == null){
            throw new RuntimeException("获取的注册中心地址有误");
        }
        ZkClient zkClient = new ZkClient(rigCenterAddress, 5000, 2000);
        //若不存在根节点先创建根节点,持久节点
        String rootPath = ExposeAll.rootPath;
        if (!zkClient.exists(rootPath)) {
            zkClient.createPersistent(rootPath,rigCenterAddress);
        }
        //若不存在相应服务父节点,则先创建,持久节点
        String serverType = serverAddressKey.substring(0, serverAddressKey.indexOf("_"));
        String serverPath = rootPath + LoadConfig.getConfigValue(serverType + "_server_name");
        System.out.println("serverPath:"+serverPath);
        if (!zkClient.exists(serverPath)) {
                //其子节点才携带具体地址
                zkClient.createPersistent(serverPath);
         }
        String serverAddress = LoadConfig.getConfigValue(serverAddressKey);
        String[] addressArr = serverAddress.split(":");
        if(addressArr.length != 2
                || addressArr[0] == null || addressArr[1] == null){
            throw new RuntimeException("获取" + serverAddress + "地址有误");
        }

        //若不存在相应服务节点,则先删除再创建,临时节点
        String nodePath = serverPath + "/" + serverAddressKey;
        if (zkClient.exists(nodePath)) {
            zkClient.delete(nodePath);
         }
        zkClient.createEphemeral(nodePath, serverAddress);
        System.out.println(nodePath + "服务注册成功");
    }
  • 服务发现与负载均衡(轮询)
package testZookeeper.testZKServer;

import org.junit.Test;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.Socket;
import java.util.*;

public class ServiceProxyFactory {

    //private static Class consumer;

    public static Object getService(Class serviceClazz){
        return Proxy.newProxyInstance(serviceClazz.getClassLoader(), new Class[]{serviceClazz},
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

                        Object back = null;
                        String[] path = getPath(serviceClazz);
                        if(null == path || path.length != 2
                                || path[0] == null || path[1] == null){
                            throw new RuntimeException("获取的地址有误");
                        }
                        System.out.println((Arrays.asList(path)));
                        String ip = path[0];
                        int port;
                        try {
                            port = Integer.parseInt(path[1]);
                        } catch (NumberFormatException e) {
                            throw new RuntimeException("获取的端口有误");
                        }
                        ObjectOutputStream objOutStream = null;
                        Socket client;
                        try {
                            client = new Socket(ip,port);
                            objOutStream = new ObjectOutputStream(client.getOutputStream());
                            objOutStream.writeUTF(method.getName());
                            objOutStream.writeObject(method.getParameterTypes());
                            objOutStream.writeObject(args);

                            try (ObjectInputStream objInStream = new ObjectInputStream(client.getInputStream())) {
                                back = objInStream.readObject();
                            } catch (ClassNotFoundException e) {
                                e.printStackTrace();
                            }
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                        finally {
                            try {
                                if(null != objOutStream)
                                    objOutStream.close();
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        }
                        return back;
                    }
                });
    }

    /**
     * 根据配置去注册中心查找服务地址
     * @return ip和端口
     */
    private static String[] getPath(Class consumer){
        try {
            //用负载均衡算法获取具体服务地址
            String ipAndPort = getServerAddress(consumer);
            //为简化,假设ip和端口格式合法,暂不做格式校验
            System.out.println("具体服务地址为:" + ipAndPort);
            return ipAndPort.split(":");
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
    // 服务调用次数
    private static int count = 1;

	//客户端实现轮询
    private static String getServerAddress(Class consumer) {
        //serversMap;
        System.out.println("App.serversMap=" + App.serversMap);
        List<String> serverList = App.serversMap.get(getFatherServerNode(consumer));
        String balancingAlgorithms = LoadConfig.getConfigValue("load_balancing_algorithms");
        //轮询算法
        if("polling".equals(balancingAlgorithms)){

            String serverAddress = serverList.get(count % serverList.size());
            count++;
            return serverAddress;
        }
        //其他情况,取首
        return serverList.get(0);
    }

    @Test
    public void test(){
        System.out.println(App.serversMap.get(getFatherServerNode(GoodService.class)));
    }

    private static String getFatherServerNode(Class consumerClazz) {
        String clazzName = consumerClazz.getName();
        String serverName = clazzName.substring(clazzName.lastIndexOf(".")+1);
        String serverKey = serverName.substring(0,serverName.indexOf("Service")).toLowerCase() + "_server_name";
        return LoadConfig.getConfigValue(serverKey).replace("/","");
    }
}


  • 配置文件
#注册中心根路径
root_node_name=/rigister_center
#注册中心地址
server_rigister_center=127.0.0.1:2181
#user服务父节点路径
user_server_name=/user_server_address
#user服务主机地址1
user_server_address_1=127.0.0.1:6060
#user服务主机地址2
user_server_address_2=127.0.0.1:6061
#user服务主机地址3
user_server_address_3=127.0.0.1:6062

#order服务父节点路径(相对根节点)
order_server_name=/order_server_address
#order服务主机地址1
order_server_address_1=127.0.0.1:7070
#order服务主机地址2
order_server_address_2=127.0.0.1:7071

#good服务父节点路径(相对根节点)
good_server_name=/good_server_address
#good服务主机地址1
good_server_address_1=127.0.0.1:9090
#good服务主机地址2
good_server_address_2=127.0.0.1:9091

#负载均衡算法类型,本例只实现了一中
load_balancing_algorithms=polling
  • zokeeper节点结构

在这里插入图片描述

  • 所有代码截图,没有模块化,时间有限请谅解
    在这里插入图片描述
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值