Zookeeperに基づくサービス登録と検出の実装

Zookeeperについて詳細に学んだ後、Zookeeperに基づいてサービス登録とディスカバリーを実装するために自分でそれを行うことができます。最初に、注文サービス(OrderService)と製品サービス(ProductService)の2つのマイクロサービスを確立する必要があります。サービスを注文するときは、製品サービスを呼び出す必要があります。


まず、製品サービス(ProductService)がZookeeperに登録される方法を見てみましょう。最初に、SpringBootプロジェクトをビルドします。
ここに画像の説明を挿入

サービスが開始したら、Zookeeperにサービスを登録する必要があります。ここでは、サーブレットAPIを使用する必要があります。ServletContextListenerServletContextオブジェクトのライフサイクルを監視できるインターフェースは、実際にはWebアプリケーションのライフサイクルを監視しています。


サーブレットコンテナがWebアプリケーションを起動または終了すると、ServletContextListenerによって処理されるServletContextEventイベントがトリガーされます。ServletContextEventイベントを処理する2つのメソッドは、ServletContextListenerインターフェースで定義されています。
ここに画像の説明を挿入

@WebListener
public class InitListener implements ServletContextListener {

    @Override
    public void contextInitialized(ServletContextEvent sce) {
        try {
            Properties properties =  new Properties();
            properties.load(InitListener.class.getClassLoader().getResourceAsStream("application.properties"));

            String hostAddress = InetAddress.getLocalHost().getHostAddress();
            int port = Integer.valueOf(properties.getProperty("server.port"));
            ServiceRegister.register(hostAddress,port);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {

    }
}

ここでは、web.xmlからannotationsまで、Webの3つの主要コンポーネントで簡単に紹介し@WebListener注釈を使用しますが、ここではSpringBootスタートアップクラスに注釈を追加することを忘れないでください@ServletComponentScan
ここに画像の説明を挿入
ここに画像の説明を挿入


もちろん、別の方法を使用することもできます。次のよう@WebListener @ServletComponentScanに、SpringBootスタートアップクラスで直接アノテーションを使用する@Beanこともできます。
ここに画像の説明を挿入



次に、ZookeeperのInitListenerクラスに登録する方法を詳しく見てみましょう。ここでは、最初にプロジェクトの構成ファイルapplication.propertiesを取得しました。取得方法についてJavaでのファイルパスの紹介多すぎます方法


もちろん、アノテーション@ComponentをSpring環境に追加し、@ Valueを使用して構成ファイル内のサービスポート番号を取得することもできます。
ここに画像の説明を挿入
ここに画像の説明を挿入


上記では、ポート番号だけでなくIPアドレスも含めて、IP + PORTをZookeeperに登録する必要があります。ここでは、IP + PORT情報を格納するために以前に導入したノードについて考える必要があります。ここで、前に紹介した3つのクライアントのいずれか、ここではCuratorを選択します。これは、ネイティブクライアントに対するこのクライアントの利点です。以前に詳しく説明しましたが、ここでは関連する依存関係を紹介する必要があります

<dependency>
    <groupId>org.apache.zookeeper</groupId>
    <artifactId>zookeeper</artifactId>
    <version>3.4.12</version>
    <exclusions>
        <exclusion>
            <artifactId>slf4j-log4j12</artifactId>
            <groupId>org.slf4j</groupId>
        </exclusion>
    </exclusions>
</dependency>

<!--对zookeeper的底层api的一些封装-->
<dependency>
    <groupId>org.apache.curator</groupId>
    <artifactId>curator-framework</artifactId>
    <version>4.3.0</version>
</dependency>
<!--封装了一些高级特性,如:Cache事件监听、选举、分布式锁、分布式Barrier-->
<dependency>
    <groupId>org.apache.curator</groupId>
    <artifactId>curator-recipes</artifactId>
    <version>4.3.0</version>
</dependency>

ここで少し注意が必要なのは、Zookeeperでslf4jが必要なことです。これは、SpringBootのslf4jのバージョンと競合するため、Zookeeperノードに情報を登録するのは非常に簡単です。ネイティブクライアントを使用する場合は、次に、レイヤーごとに親ノードの存在を判断するなどの追加の操作が必要です。そうでない場合は、親ノードを作成し、クライアントに接続するときにCountDownLatchとWatchを使用して接続が成功するまで待機する必要があります。

public class ServiceRegister {

    private static final String CONNENT_ADDR = "127.0.0.1:2181";
    private static final String BASE_SERVICES = "/services";
    private static final String SERVICE_NAME = "/products";

    public static void register(String address, int port) throws Exception {
        CuratorFramework curatorFramework = CuratorFrameworkFactory.builder()
                .connectString(CONNENT_ADDR)
                .connectionTimeoutMs(5000)  //连接超时时间
                .sessionTimeoutMs(5000)     //会话超时时间
                .retryPolicy(new ExponentialBackoffRetry(1000, 3))
                .build();
        curatorFramework.start();

        String path = BASE_SERVICES + SERVICE_NAME;
        String server_path = address + ":" + port;

        curatorFramework.create()
                .creatingParentsIfNeeded()
                .withMode(CreateMode.EPHEMERAL_SEQUENTIAL)
                .forPath(path + "/child", server_path.getBytes());
    }
}

次に、製品サービスの作業はほぼ完了しました。application.propertiesのポート番号を変更して、複数の製品サービスを開始できます。製品サービスが外部に提供するインターフェースは、次のとおりです。これは、サービスのポート番号を返すだけです。
ここに画像の説明を挿入




製品サービスの登録が完了したら、次のステップは間違いなく注文サービスの発見です。最初に、SpringBootプロジェクトも作成する必要があります。同様に、サーブレットAPIのServletContextListenerインターフェイスを使用する必要があります
ここに画像の説明を挿入
ここに画像の説明を挿入

製品サービスとは異なり、製品サービスのInitListenerクラスは、サーブレットコンテナーの開始時に製品サービスのIP + PORT情報をZookeeperに登録し、注文サービスのInitListenerは、サーブレットコンテナーの開始時に開始します。 Zookeeperに登録されている製品サービス情報は、注文サービスが呼び出すためにプルダウンされます

public class InitListener implements ServletContextListener {

    private static final String CONNENT_ADDR = "127.0.0.1:2181";
    private static final String BASE_SERVICES = "/services";
    private static final String SERVICE_NAME = "/products";

    @Override
    public void contextInitialized(ServletContextEvent sce) {
        CuratorFramework curatorFramework = CuratorFrameworkFactory.builder()
                .connectString(CONNENT_ADDR)
                .connectionTimeoutMs(5000)  //连接超时时间
                .sessionTimeoutMs(5000)     //会话超时时间
                .retryPolicy(new ExponentialBackoffRetry(1000, 3))
                .build();
        curatorFramework.start();

        List<String> newServerList = new ArrayList<>();
        String path = BASE_SERVICES + SERVICE_NAME;
        final PathChildrenCache cache = new PathChildrenCache(curatorFramework, path, true);
        try {
            cache.start(PathChildrenCache.StartMode.POST_INITIALIZED_EVENT);
            cache.getListenable().addListener(new PathChildrenCacheListener() {
                //监听子节点的变化
                @Override
                public void childEvent(CuratorFramework cf, PathChildrenCacheEvent event) throws Exception {
                    List<String> list = curatorFramework.getChildren().forPath(path);
                    for (String subNode : list) {
                        byte[] bytes = curatorFramework.getData().forPath(path + "/" + subNode);
                        String host = new String(bytes, "utf-8");
                        newServerList.add(host);
                    }
                    LoadBalance.SERVICE_LIST = newServerList;
                }
            });
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
    }
}

ここでは、Zookeeper上のノードの変更を監視する必要があります。これは、製品サービスがダウンした場合や新しい製品サービスの場合に、時間内に情報を取得できる必要があるためです。キュレーターモニタリングの概要については、Zookeeperクライアント(5)-Curatorを参照してください
ここに画像の説明を挿入


ノードの変更をリッスンすると、主に負荷分散の簡単なシミュレーションであるLoadBalanceクラスの値が更新されます。ここでは、注文サービスが製品サービスを呼び出すときに、すべての製品サービスからサービスをランダムに選択しています

public class LoadBalance {

    public volatile static List<String> SERVICE_LIST;

    public String choseService() {
        String result = "";
        if (!CollectionUtils.isEmpty(SERVICE_LIST)) {
            int index = new Random().nextInt(SERVICE_LIST.size());
            result = SERVICE_LIST.get(index);
        }
        return result;
    }
}

これで、Zookeeperに基づくサービスの登録と検出が基本的に完了します。次に、注文サービスでコントローラーも提供します。コントローラーは、製品サービスのコントローラーを呼び出します。ここでは、RestTemplateを次のように使用します。

@RestController
@RequestMapping("/order")
public class OrderController {

    @Resource
    private RestTemplate restTemplate;

    private LoadBalance loadBalance = new LoadBalance();

    @RequestMapping("/getOrder")
    public Object getProduct(HttpServletRequest request) {
        String host = loadBalance.choseService();
        return restTemplate.getForObject("http://" + host + "/product/getProduct", Object.class);
    }
}

ここに画像の説明を挿入




以上で簡単なサービスの登録と検出が完了しました。ここでテストしてみましょう。製品サービス(ProductService)のapplicat.propertiesファイルでポート番号を変更して、複数の製品サービスを開始できます。ここでは8080を開始します、8081 2つのサービスサービス、ZooInspectorを使用して、Zookeeperのノード情報を次のように表示できます。
ここに画像の説明を挿入ここに画像の説明を挿入


次に、注文サービスを開始し、常にブラウザで更新しています。さまざまな製品サービスがランダムに呼び出されることがわかります
ここに画像の説明を挿入
ここに画像の説明を挿入

286の元の記事が公開されました Liked12 訪問者10,000以上

おすすめ

転載: blog.csdn.net/newbie0107/article/details/105442764