ZooKeeper实战(二)--简单的服务发现与注册

发布订阅

发布/订阅模式是一对多的关系,多个订阅者对象同时监听某一主题对象,这个主题对象在自身状态发生变化时会通知所有的订阅者对象。使它们能自动的更新自己的状态。发布/订阅可以使得发布方和订阅方独立封装、独立改变。当一个对象的改变需要同时改变其他对象,而且它不知道具体有多少对象需要改变时可以使用发布/订阅模式。发布/订阅模式在分布式系统中的典型应用有服务发现与注册。

服务发现与注册

服务发现与注册是指对集群中的服务上下线做统一管理。每个工作服务器都可以作为数据的发布方向集群注册自己的基本信息,而让某些监控服务器作为订阅方,订阅工作服务器的基本信息,当工作服务器的基本信息发生改变如上下线、服务器角色或服务范围变更,监控服务器可以得到通知并响应这些变化。

ZK实现DEMO

服务提供方

public class InitListener implements ServletContextListener {

    @Value("${server.port}")
    private int port;

    @Override
    public void contextInitialized(ServletContextEvent sce) {
        WebApplicationContextUtils.getRequiredWebApplicationContext(sce.getServletContext()).getAutowireCapableBeanFactory().autowireBean(this);
        try {
            // 获取本机ip
            String hostAddress = InetAddress.getLocalHost().getHostAddress();
            // 服务注册
            ServiceRegister.register(hostAddress,port);
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {

    }
}
public class ServiceRegister {

    private static final String BASE_SERVICES = "/services";
    private static final String SERVICE_NAME = "/service1";

    public static void register(String address,int port) {
        try {
            ZooKeeper zooKeeper = new ZooKeeper("127.0.0.1:2181",5000,(watchedEvent)->{});
            Stat exists = zooKeeper.exists(BASE_SERVICES + SERVICE_NAME, false);
            if(exists==null) {
                // 如果service1不存在,创建节点
                zooKeeper.create(BASE_SERVICES + SERVICE_NAME,"".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
            }
            String server_path = address+":"+port;
            // 创建临时子节点
            zooKeeper.create(BASE_SERVICES + SERVICE_NAME+"/child",server_path.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE,CreateMode.EPHEMERAL_SEQUENTIAL);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

这里通过ZK临时节点的方式进行服务注册,当ZK连接断开后,临时节点会自动销毁。

 

服务消费方

public class InitListener implements ServletContextListener {

    private static final String BASE_SERVICES = "/services";
    private static final String SERVICE_NAME="/service1";

    private ZooKeeper zooKeeper;
    
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        try {
             zooKeeper = new ZooKeeper("127.0.0.1:2181",5000,(watchedEvent)->{ 
                 // 监听service1变化
                 if(watchedEvent.getType() == Watcher.Event.EventType.NodeChildrenChanged  && watchedEvent.getPath().equals(BASE_SERVICES+SERVICE_NAME)) { 
                     updateServiceList(); 
                 } 
             });
             updateServiceList();
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    // 更新实例列表
    private void updateServiceList() {
       try{
           List<String> children = zooKeeper.getChildren(BASE_SERVICES + SERVICE_NAME, true);
           List<String> newServerList = new ArrayList<String>();
           for(String subNode:children) {
               // 获取子节点
               byte[] data = zooKeeper.getData(BASE_SERVICES  + SERVICE_NAME + "/" + subNode, false, null);
               String host = new String(data, "utf-8");
               System.out.println("host:"+host);
               newServerList.add(host);
           }
           LoadBalance.SERVICE_LIST = newServerList;
       }catch (Exception e) {
           e.printStackTrace();
       }
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {

    }
}
public abstract class LoadBalance {

    // 实例列表
    public volatile static List<String> SERVICE_LIST;

    // 负载均衡
    public abstract String choseServiceHost();

}
public class RamdomLoadBalance extends LoadBalance {

    @Override
    public String choseServiceHost() {
        // 实现随机负载均衡
        String result = "";
        if(!CollectionUtils.isEmpty(SERVICE_LIST)) {
            int index = new Random().nextInt(SERVICE_LIST.size());
            result = SERVICE_LIST.get(index);
        }
        return result ;
    }
}
@RequestMapping("/api/demo")
@RestController
public class ConsumerController {

    @Resource
    private RestTemplate restTemplate;

    private LoadBalance loadBalance = new RamdomLoadBalance();

    @RequestMapping("/consumer")
    public Object doConsumer() {
        return restTemplate.getForObject("http://"+loadBalance.choseServiceHost()+"/api/demo/provider", Result.class);
    }
}

消费者通过监听ZK节点变化,获取服务列表,通过负载均衡算法调用不同的节点。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值