zookeeper实战:服务注册于发现

依赖

        <dependency>
            <groupId>org.apache.zookeeper</groupId>
            <artifactId>zookeeper</artifactId>
            <version>3.4.9</version>
        </dependency>
     
        <dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-framework</artifactId>
            <version>4.0.1</version>
        </dependency>

        <dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-recipes</artifactId>
            <version>4.0.1</version>
        </dependency>

服务注册实现

注册类

public class RgisteToZk implements ApplicationRunner, ApplicationContextAware {


    private static final Logger LOG = Logger.getLogger(RgisteToZk.class);

    //ZooKeeper服务地址
    private static final String SERVER = "127.0.0.1:2181";

    //会话超时时间
    private static final int SESSION_TIMEOUT = 30000;

    //连接超时时间
    private static final int CONNECTION_TIMEOUT = 5000;

    //创建连接实例
    private CuratorFramework client = null;

    //服务注册的节点路径
    private static final String BASE_SERVICES = "/services";

    //需要注册的服务
    private static final List<String> services = new LinkedList<>();

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

    @PostConstruct
    public void init() {
        //获取zk连接实例
        client = CuratorFrameworkFactory.newClient(SERVER, SESSION_TIMEOUT, CONNECTION_TIMEOUT, new ExponentialBackoffRetry(1000, 3));

        //启动
        client.start();
    }

    @PreDestroy
    public void destory() {
        //关闭
        client.delete();
        client.close();
    }


    //注册服务
    public void registe() {
        try {
            Stat pathStat = client.checkExists().forPath(BASE_SERVICES);
            if (pathStat == null) {
                //创建根路径
                client.create().orSetData().withMode(CreateMode.PERSISTENT).forPath(BASE_SERVICES, "services".getBytes());
            }

            services.forEach(service -> {
                try {
                    //创建临时有顺序的节点,因为存在节点挂掉和节点服务一致的情况,所以需要临时并且有顺序
                    client.create().orSetData().withMode(CreateMode.EPHEMERAL_SEQUENTIAL).forPath(BASE_SERVICES + "/", service.getBytes());
                } catch (Exception e) {
                    e.printStackTrace();
                    LOG.error("注册失败!");
                }
            });
        } catch (Exception e) {
            e.printStackTrace();
            LOG.error("节点创建失败!");
        }
    }

    @Override
    public void run(ApplicationArguments args) {
        registe();
    }

    //获取服务
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        String address = "";
        try {
            address = InetAddress.getLocalHost().getHostAddress() + ":" + port;
        } catch (UnknownHostException e) {
            e.printStackTrace();
        }
         //获取容器中所有的服务
        String[] allService = applicationContext.getBeanNamesForAnnotation(RestController.class);
        for (int i = 0; i < allService.length; i++) {
            String service = allService[i];
            Class bean = applicationContext.getBean(service).getClass();
            Method[] methods = bean.getMethods();
            for (Method method : methods) {
                GetMapping annotation = method.getAnnotation(GetMapping.class);
                if (annotation != null) {
                    String[] path = annotation.value();
                    services.add(address + "/" + path[0]);
                }
            }
        }
    }
}

注册启动注解

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(RgisteToZk.class)
public @interface EnableServiceRgisteToZk {

}

实体类

public class User {

    private String userName;
    private int age;

    public User(String userName, int age) {
        this.userName = userName;
        this.age = age;
    }

    public String getUserName() {
        return userName;
    }

    public User setUserName(String userName) {
        this.userName = userName;
        return this;
    }

    public int getAge() {
        return age;
    }

    public User setAge(int age) {
        this.age = age;
        return this;
    }
}

服务类

@RestController
public class ProductController {

    @GetMapping("product")
    public String product() {
        return "test";
    }
}

启动类

@SpringBootApplication
@EnableServiceRgisteToZk
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

}

服务发现

服务发现类

//拉取服务
public class PullServices implements ApplicationRunner {


    private static final Logger LOG = Logger.getLogger(PullServices.class);

    //ZooKeeper服务地址
    private static final String SERVER = "127.0.0.1:2181";

    //会话超时时间
    private static final int SESSION_TIMEOUT = 30000;

    //连接超时时间
    private static final int CONNECTION_TIMEOUT = 5000;

    //创建连接实例
    private CuratorFramework client = null;

    //服务注册的节点路径
    private static final String BASE_SERVICES = "/services";

    public static final Map<String, String> services = new ConcurrentHashMap<>();


    @PostConstruct
    public void init() {
        //获取zk连接实例
        client = CuratorFrameworkFactory.newClient(SERVER, SESSION_TIMEOUT, CONNECTION_TIMEOUT, new ExponentialBackoffRetry(1000, 3));

        //启动
        client.start();
    }

    @PreDestroy
    public void destory() {
        //关闭
        client.close();
    }


    private void pull() throws Exception {

        List<String> paths = client.getChildren().forPath(BASE_SERVICES);
        if (paths == null || paths.size() == 0) {
            LOG.error("服务不存在!");
            throw new NullPointerException();
        }


        paths.forEach(path -> {
            try {
                byte[] bytes = client.getData().forPath(BASE_SERVICES + "/" + path);
                String pathData = new String(bytes, "UTF-8");
                services.put(path, pathData);

                //监听当前节点
                TreeCache treeCache = new TreeCache(client, "/test");
                //设置监听器和处理过程
                treeCache.getListenable().addListener(new TreeCacheListener() {
                    public void childEvent(CuratorFramework client, TreeCacheEvent event) throws Exception {
                        ChildData data = event.getData();
                        if (data != null) {
                            String currentPath = data.getPath();
                            String pathData = new String(data.getData(), "UTF-8");
                            switch (event.getType()) {
                                case NODE_ADDED:
                                    services.put(currentPath, pathData);
                                    break;
                                case NODE_REMOVED:
                                    services.remove(currentPath, pathData);
                                    break;
                                case NODE_UPDATED:
                                    services.put(currentPath, pathData);
                                    break;

                                default:
                                    break;
                            }
                        } else {
                            LOG.error("data is null : " + event.getType());
                        }
                    }
                });
                //开始监听
                treeCache.start();

            } catch (Exception e) {
                e.printStackTrace();
            }
        });


    }


    @Override
    public void run(ApplicationArguments args) throws Exception {
        pull();
    }
}

开启服务发现注解

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(PullServices.class)
public @interface EnablePullServices {
}

自定义负载工具类

public abstract class LoadBalance {

    public final List<String> SERVICE_LIST;

    protected LoadBalance(List<String> service_list) {
        SERVICE_LIST = service_list;
    }

    public abstract String choseServiceHost();
    
}
public class RamdomLoadBalance extends LoadBalance {
    public RamdomLoadBalance(List<String> service_list) {
        super(service_list);
    }

    @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;
    }
}

调用类

@RestController
public class UserController {

    @Autowired
    private RestTemplate restTemplate;
    private static final ConcurrentHashMap concurrentHashMap = (ConcurrentHashMap) PullServices.services;

    @GetMapping("test")
    public String getUser() {
        List<String> services = new LinkedList<>();
        concurrentHashMap.forEach((key, value) -> {
            services.add(((String) value));
        });
        LoadBalance balance = new RamdomLoadBalance(services);
        String host = balance.choseServiceHost();
        User user = restTemplate.getForObject("http://" + host, User.class);
        return host + ".........." + user;
    }

}

实体类

public class User implements Serializable {

    private String userName;
    private int age;

    public User(){}

    public User(String userName, int age) {
        this.userName = userName;
        this.age = age;
    }

    public String getUserName() {
        return userName;
    }

    public User setUserName(String userName) {
        this.userName = userName;
        return this;
    }

    public int getAge() {
        return age;
    }

    public User setAge(int age) {
        this.age = age;
        return this;
    }

    @Override
    public String toString() {
        return "User{" +
                "userName='" + userName + '\'' +
                ", age=" + age +
                '}';
    }
}

启动类

@SpringBootApplication
@EnablePullServices
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }

}

测试结果

服务注册

在这里插入图片描述
在这里插入图片描述

服务发现

在这里插入图片描述
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_28822933/article/details/85936985