简易智能识别服务3.2—springboot 集成zookeeper(curator)实现注册发现

2018年10月28日

智能识别服务涉及调用zookeeper做分布式消息同步,使用curator 包(对zookeeper的简单封装)提供了一些集成的方法,  比zookeeper原生的Client好用很多,提供了很多功能包括:

  • 自动化的连接管理: 重新建立到ZooKeeper的连接和重试机制存在一些潜在的错误case。 Curator帮助你处理这些事情,对你来说是透明的。
  • 清理API:
    • 简化了原生的ZooKeeper的方法,事件等
    • 提供了一个现代的流式接口
  • 提供了Recipes实现:基于这些Recipes可以创建很多复杂的分布式应用

 CuratorFramework实例都是线程安全的,可以让我们的应用中共享同一个CuratorFramework实例.

1.定义ZkClient 类

public class ZkClient {
    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    private CuratorFramework client;
    private String zookeeperServer;
    private int sessionTimeoutMs;
    private int connectionTimeoutMs;
    private int baseSleepTimeMs;
    private int maxRetries;

    ...

    public void init() {
        RetryPolicy retryPolicy = new ExponentialBackoffRetry(baseSleepTimeMs, maxRetries);
        client = CuratorFrameworkFactory.builder().connectString(zookeeperServer).retryPolicy(retryPolicy)
                .sessionTimeoutMs(sessionTimeoutMs).connectionTimeoutMs(connectionTimeoutMs).build();
        client.start();
    }

    public void stop() {
        client.close();
    }

   ...

    public void register(String path) {
        try {
            String rootPath = "/" + path;
            String hostAddress = InetAddress.getLocalHost().getHostAddress();
            String serviceInstance = "prometheus" + "-" +  hostAddress + "-";
            client.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL_SEQUENTIAL).forPath(rootPath + "/" + serviceInstance);
        } catch (Exception e) {
            logger.error("注册出错", e);
        }
    }

}

2.  定义configuration,参数从配置文件中获取 & 定义ZKClient的Bean交给Spring管理

并定义一个ZKClient类的Bean对象给Spring 管理

@Configuration
public class ZkConfiguration {
    @Value("${zookeeper.server}")
    private String zookeeperServer;
    @Value(("${zookeeper.sessionTimeoutMs}"))
    private int sessionTimeoutMs;
    @Value("${zookeeper.connectionTimeoutMs}")
    private int connectionTimeoutMs;
    @Value("${zookeeper.maxRetries}")
    private int maxRetries;
    @Value("${zookeeper.baseSleepTimeMs}")
    private int baseSleepTimeMs;

    @Bean(name="MyZkClient",initMethod = "init", destroyMethod = "stop")
    //@Scope("prototype")
    public ZkClient zkClient() {
        ZkClient zkClient = new ZkClient();
        zkClient.setZookeeperServer(zookeeperServer);
        zkClient.setSessionTimeoutMs(sessionTimeoutMs);
        zkClient.setConnectionTimeoutMs(connectionTimeoutMs);
        zkClient.setMaxRetries(maxRetries);
        zkClient.setBaseSleepTimeMs(baseSleepTimeMs);
        return zkClient;
    }

配置文件:

#============== zookeeper ===================
zookeeper.server=x.x.x.x:2181
zookeeper.sessionTimeoutMs=6000
zookeeper.connectionTimeoutMs=6000
zookeeper.maxRetries=3
zookeeper.baseSleepTimeMs=1000

3. 获取ZKclient的Bean实例

在Spring主函数入口获取

@SpringBootApplication
@ComponentScan("ai/com/XX")//包名
@EnableZuulProxy
@Controller
public class Application {
    public static void main(String[] args) {
        ApplicationContext context = SpringApplication.run(Application.class, args);
        ZkClient zkClient = context.getBean(ZkClient.class);
        zkClient.register();
    }
}

为了更通用, 可以定义一个SpringUtil获取专门获取Bean对象, 可以通过Name,Class等多种方式获取

@Component
public class SpringUtil implements ApplicationContextAware {

    private static ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        if(SpringUtil.applicationContext == null) {
            SpringUtil.applicationContext = applicationContext;
        }
        System.out.println("========ApplicationContext配置成功,在普通类可以通过调用SpringUtils.getAppContext()获取applicationContext对象,applicationContext="+SpringUtil.applicationContext+"========");

    }

    //获取applicationContext
    public static ApplicationContext getApplicationContext() {
        return applicationContext;
    }

    //通过name获取 Bean.
    public static Object getBean(String name){
        return getApplicationContext().getBean(name);
    }

    //通过class获取Bean.
    public static <T> T getBean(Class<T> clazz){
        return getApplicationContext().getBean(clazz);
    }

    //通过name,以及Clazz返回指定的Bean
    public static <T> T getBean(String name,Class<T> clazz){
        return getApplicationContext().getBean(name, clazz);
    }

}

在自己的普通业务类中使用SpringUtil获取 已经定义的Bean实例:

   public ConfigManager(String connectString,boolean autoReconneted) {
        this.connectString=connectString;
        this.autoReconnected = autoReconneted;
        try {
            MyzkClient = (ZkClient) SpringUtil.getBean("MyZkClient");
            zkClient = MyzkClient.getClient();
            System.out.println("Reconnected success!...");
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }

 

 


Spring 依赖注入, 实现原理:

类实例的生命周期(创建, 删除)不再由 应用程序管理, 而是由外部框架Spring负责

Spring通过读取bean配置XML 或者 通过扫描注解的方式, 读取Bean 的特征, 并根据特征名称,找到类, 然后 通过类 在创建实例注入到 依赖的  实例变量中;

Spring Bean的依赖注入原理个人理解如下图:

当一个BeanID对应 一个BeanInstance时就是 单例 singleton模式,  也是默认的模式

没有评论

发表评论

邮箱地址不会被公开。 必填项已用*标注