You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@shenyu.apache.org by yu...@apache.org on 2022/10/12 12:15:25 UTC

[shenyu] branch master updated: [type:feat] ZookeeperInstanceRegisterRepository and HttpClientShenyuHttpClient. (#3930) (#3965)

This is an automated email from the ASF dual-hosted git repository.

yunlong pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/shenyu.git


The following commit(s) were added to refs/heads/master by this push:
     new 6a4f4e7bc [type:feat]  ZookeeperInstanceRegisterRepository and HttpClientShenyuHttpClient. (#3930) (#3965)
6a4f4e7bc is described below

commit 6a4f4e7bc2b9b113217606bb76c93248649a949f
Author: 杨文杰 <31...@users.noreply.github.com>
AuthorDate: Wed Oct 12 20:15:14 2022 +0800

    [type:feat]  ZookeeperInstanceRegisterRepository and HttpClientShenyuHttpClient. (#3930) (#3965)
    
    * Implementation ShenyuInstanceRegisterRepository#selectInstancesAndWatcher in ZookeeperInstanceRegisterRepository and Implementation HttpClientShenyuHttpClient
    
    * [type:feat] shenyu-sdk-httpclient and shenyu-register-instance-zookeeper#selectInstancesAndWatcher. #3930
    
    * Implementation ShenyuInstanceRegisterRepository#selectInstancesAndWatcher in ZookeeperInstanceRegisterRepository and Implementation HttpClientShenyuHttpClient
    
    * Implementation ShenyuInstanceRegisterRepository#selectInstancesAndWatcher in ZookeeperInstanceRegisterRepository and Implementation HttpClientShenyuHttpClient
    
    * Implementation ShenyuInstanceRegisterRepository#selectInstancesAndWatcher in ZookeeperInstanceRegisterRepository and Implementation HttpClientShenyuHttpClient
    
    * Implementation ShenyuInstanceRegisterRepository#selectInstancesAndWatcher in ZookeeperInstanceRegisterRepository and Implementation HttpClientShenyuHttpClient
    
    * Implementation ShenyuInstanceRegisterRepository#selectInstancesAndWatcher in ZookeeperInstanceRegisterRepository and Implementation HttpClientShenyuHttpClient
---
 .../instance/zookeeper/ZookeeperClient.java        |  15 +++
 .../ZookeeperInstanceRegisterRepository.java       |  35 ++++++-
 .../ZookeeperInstanceRegisterRepositoryTest.java   |  45 +++++++-
 shenyu-sdk/pom.xml                                 |   1 +
 shenyu-sdk/{ => shenyu-sdk-httpclient}/pom.xml     |  35 +++++--
 .../sdk/httpclient/HttpClientShenyuHttpClient.java | 113 +++++++++++++++++++++
 .../httpclient/HttpClientShenyuHttpClientTest.java |  45 ++++++++
 7 files changed, 278 insertions(+), 11 deletions(-)

diff --git a/shenyu-register-center/shenyu-register-instance/shenyu-register-instance-zookeeper/src/main/java/org/apache/shenyu/register/instance/zookeeper/ZookeeperClient.java b/shenyu-register-center/shenyu-register-instance/shenyu-register-instance-zookeeper/src/main/java/org/apache/shenyu/register/instance/zookeeper/ZookeeperClient.java
index 15ce67e88..3812073cf 100644
--- a/shenyu-register-center/shenyu-register-instance/shenyu-register-instance-zookeeper/src/main/java/org/apache/shenyu/register/instance/zookeeper/ZookeeperClient.java
+++ b/shenyu-register-center/shenyu-register-instance/shenyu-register-instance-zookeeper/src/main/java/org/apache/shenyu/register/instance/zookeeper/ZookeeperClient.java
@@ -20,6 +20,7 @@ package org.apache.shenyu.register.instance.zookeeper;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.curator.framework.CuratorFramework;
 import org.apache.curator.framework.CuratorFrameworkFactory;
+import org.apache.curator.framework.api.CuratorWatcher;
 import org.apache.curator.framework.recipes.cache.ChildData;
 import org.apache.curator.framework.recipes.cache.TreeCache;
 import org.apache.curator.framework.recipes.cache.TreeCacheListener;
@@ -236,6 +237,20 @@ public class ZookeeperClient {
         return cache;
     }
 
+    /**
+     * add children watcher.
+     * @param key selectKey
+     * @param curatorWatcher watcher
+     * @return children List
+     */
+    public List<String> subscribeChildrenChanges(final String key, final CuratorWatcher curatorWatcher) {
+        try {
+            return client.getChildren().usingWatcher(curatorWatcher).forPath(key);
+        } catch (Exception e) {
+            throw new ShenyuException(e);
+        }
+    }
+
     /**
      * find cache with  key.
      * @param key key.
diff --git a/shenyu-register-center/shenyu-register-instance/shenyu-register-instance-zookeeper/src/main/java/org/apache/shenyu/register/instance/zookeeper/ZookeeperInstanceRegisterRepository.java b/shenyu-register-center/shenyu-register-instance/shenyu-register-instance-zookeeper/src/main/java/org/apache/shenyu/register/instance/zookeeper/ZookeeperInstanceRegisterRepository.java
index ee78fe896..9f869665e 100644
--- a/shenyu-register-center/shenyu-register-instance/shenyu-register-instance-zookeeper/src/main/java/org/apache/shenyu/register/instance/zookeeper/ZookeeperInstanceRegisterRepository.java
+++ b/shenyu-register-center/shenyu-register-instance/shenyu-register-instance-zookeeper/src/main/java/org/apache/shenyu/register/instance/zookeeper/ZookeeperInstanceRegisterRepository.java
@@ -18,21 +18,29 @@
 package org.apache.shenyu.register.instance.zookeeper;
 
 import org.apache.commons.lang3.StringUtils;
+import org.apache.curator.framework.api.CuratorWatcher;
 import org.apache.curator.framework.state.ConnectionState;
 import org.apache.shenyu.common.config.ShenyuConfig.InstanceConfig;
 import org.apache.shenyu.common.constant.Constants;
 import org.apache.shenyu.common.utils.GsonUtils;
 import org.apache.shenyu.register.common.dto.InstanceRegisterDTO;
 import org.apache.shenyu.register.common.path.RegisterPathConstants;
+import org.apache.shenyu.register.common.subsriber.WatcherListener;
 import org.apache.shenyu.register.instance.api.ShenyuInstanceRegisterRepository;
 import org.apache.shenyu.spi.Join;
 import org.apache.zookeeper.CreateMode;
+import org.apache.zookeeper.WatchedEvent;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.Properties;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import java.util.Objects;
+import java.util.Collections;
 
 /**
  * The type Zookeeper instance register repository.
@@ -82,7 +90,7 @@ public class ZookeeperInstanceRegisterRepository implements ShenyuInstanceRegist
 
         client.start();
     }
-    
+
     @Override
     public void persistInstance(final InstanceRegisterDTO instance) {
         String uriNodeName = buildInstanceNodeName(instance);
@@ -95,7 +103,30 @@ public class ZookeeperInstanceRegisterRepository implements ShenyuInstanceRegist
         nodeDataMap.put(realNode, nodeData);
         client.createOrUpdate(realNode, nodeData, CreateMode.EPHEMERAL);
     }
-    
+
+    @Override
+    public List<InstanceRegisterDTO> selectInstancesAndWatcher(final String selectKey, final WatcherListener watcherListener) {
+        final Function<List<String>, List<InstanceRegisterDTO>> getInstanceRegisterFun = childrenList -> childrenList.stream().map(childPath -> {
+            String instanceRegisterJsonStr = client.get(RegisterPathConstants.buildRealNode(selectKey, childPath));
+            return GsonUtils.getInstance().fromJson(instanceRegisterJsonStr, InstanceRegisterDTO.class);
+        }).collect(Collectors.toList());
+
+        List<String> childrenPathList = client.subscribeChildrenChanges(selectKey, new CuratorWatcher() {
+            @Override
+            public void process(final WatchedEvent event) throws Exception {
+                if (watcherListener != null) {
+                    String path = Objects.isNull(event.getPath()) ? "" : event.getPath();
+                    List<String> childrenList = StringUtils.isNotBlank(path) ? client.subscribeChildrenChanges(path, this)
+                            : Collections.emptyList();
+                    if (!childrenList.isEmpty()) {
+                        watcherListener.listener(getInstanceRegisterFun.apply(childrenList));
+                    }
+                }
+            }
+        });
+        return getInstanceRegisterFun.apply(childrenPathList);
+    }
+
     @Override
     public void close() {
         client.close();
diff --git a/shenyu-register-center/shenyu-register-instance/shenyu-register-instance-zookeeper/src/test/java/org/apache/shenyu/register/instance/zookeeper/ZookeeperInstanceRegisterRepositoryTest.java b/shenyu-register-center/shenyu-register-instance/shenyu-register-instance-zookeeper/src/test/java/org/apache/shenyu/register/instance/zookeeper/ZookeeperInstanceRegisterRepositoryTest.java
index c1ff22c61..220218922 100644
--- a/shenyu-register-center/shenyu-register-instance/shenyu-register-instance-zookeeper/src/test/java/org/apache/shenyu/register/instance/zookeeper/ZookeeperInstanceRegisterRepositoryTest.java
+++ b/shenyu-register-center/shenyu-register-instance/shenyu-register-instance-zookeeper/src/test/java/org/apache/shenyu/register/instance/zookeeper/ZookeeperInstanceRegisterRepositoryTest.java
@@ -18,23 +18,30 @@
 package org.apache.shenyu.register.instance.zookeeper;
 
 import org.apache.curator.framework.CuratorFramework;
+import org.apache.curator.framework.api.CuratorWatcher;
 import org.apache.curator.framework.listen.Listenable;
 import org.apache.curator.framework.state.ConnectionState;
 import org.apache.curator.framework.state.ConnectionStateListener;
 import org.apache.shenyu.common.config.ShenyuConfig;
+import org.apache.shenyu.common.utils.GsonUtils;
 import org.apache.shenyu.register.common.dto.InstanceRegisterDTO;
+import org.apache.shenyu.register.common.path.RegisterPathConstants;
+import org.apache.shenyu.register.common.subsriber.WatcherListener;
+import org.apache.zookeeper.WatchedEvent;
 import org.junit.jupiter.api.Test;
 import org.mockito.MockedConstruction;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 import java.util.Properties;
 
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.when;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.mockConstruction;
-import static org.mockito.Mockito.when;
 
 public class ZookeeperInstanceRegisterRepositoryTest {
 
@@ -64,4 +71,40 @@ public class ZookeeperInstanceRegisterRepositoryTest {
             repository.close();
         }
     }
+
+    @Test
+    public void testSelectInstancesAndWatcher() throws Exception {
+        InstanceRegisterDTO data = InstanceRegisterDTO.builder()
+                .appName("shenyu-test")
+                .host("shenyu-host")
+                .port(9195)
+                .build();
+        final Listenable listenable = mock(Listenable.class);
+        final CuratorWatcher[] watcherArr = new CuratorWatcher[1];
+
+        try (MockedConstruction<ZookeeperClient> construction = mockConstruction(ZookeeperClient.class, (mock, context) -> {
+            final CuratorFramework curatorFramework = mock(CuratorFramework.class);
+            when(mock.getClient()).thenReturn(curatorFramework);
+            when(mock.subscribeChildrenChanges(anyString(), any(CuratorWatcher.class))).thenAnswer(invocation -> {
+                Object[] args = invocation.getArguments();
+                watcherArr[0] = (CuratorWatcher) args[1];
+                return Collections.singletonList("shenyu-test");
+            });
+            when(mock.get(anyString())).thenReturn(GsonUtils.getInstance().toJson(data));
+            when(curatorFramework.getConnectionStateListenable()).thenReturn(listenable);
+        })) {
+            final ZookeeperInstanceRegisterRepository repository = new ZookeeperInstanceRegisterRepository();
+            ShenyuConfig.InstanceConfig config = new ShenyuConfig.InstanceConfig();
+            repository.init(config);
+            final Properties configProps = config.getProps();
+            configProps.setProperty("digest", "digest");
+            repository.init(config);
+            repository.selectInstancesAndWatcher(RegisterPathConstants.buildInstanceParentPath(), mock(WatcherListener.class));
+            WatchedEvent mockEvent = mock(WatchedEvent.class);
+            when(mockEvent.getPath()).thenReturn(RegisterPathConstants.buildInstanceParentPath());
+            watcherArr[0].process(mockEvent);
+            repository.close();
+        }
+    }
+
 }
diff --git a/shenyu-sdk/pom.xml b/shenyu-sdk/pom.xml
index 10666c39d..8c86e4398 100644
--- a/shenyu-sdk/pom.xml
+++ b/shenyu-sdk/pom.xml
@@ -30,5 +30,6 @@
     <modules>
         <module>shenyu-sdk-core</module>
         <module>shenyu-sdk-spring</module>
+        <module>shenyu-sdk-httpclient</module>
     </modules>
 </project>
\ No newline at end of file
diff --git a/shenyu-sdk/pom.xml b/shenyu-sdk/shenyu-sdk-httpclient/pom.xml
similarity index 58%
copy from shenyu-sdk/pom.xml
copy to shenyu-sdk/shenyu-sdk-httpclient/pom.xml
index 10666c39d..9a6c39245 100644
--- a/shenyu-sdk/pom.xml
+++ b/shenyu-sdk/shenyu-sdk-httpclient/pom.xml
@@ -19,16 +19,35 @@
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
     <parent>
+        <artifactId>shenyu-sdk</artifactId>
         <groupId>org.apache.shenyu</groupId>
-        <artifactId>shenyu</artifactId>
         <version>2.5.1-SNAPSHOT</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
-    <artifactId>shenyu-sdk</artifactId>
-    <packaging>pom</packaging>
-    
-    <modules>
-        <module>shenyu-sdk-core</module>
-        <module>shenyu-sdk-spring</module>
-    </modules>
+
+
+    <artifactId>shenyu-sdk-httpclient</artifactId>
+
+    <properties>
+        <maven.compiler.source>8</maven.compiler.source>
+        <maven.compiler.target>8</maven.compiler.target>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <artifactId>shenyu-sdk-core</artifactId>
+            <groupId>org.apache.shenyu</groupId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.httpcomponents</groupId>
+            <artifactId>httpclient</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.httpcomponents</groupId>
+            <artifactId>httpcore</artifactId>
+        </dependency>
+    </dependencies>
+
 </project>
\ No newline at end of file
diff --git a/shenyu-sdk/shenyu-sdk-httpclient/src/main/java/org/apache/shenyu/sdk/httpclient/HttpClientShenyuHttpClient.java b/shenyu-sdk/shenyu-sdk-httpclient/src/main/java/org/apache/shenyu/sdk/httpclient/HttpClientShenyuHttpClient.java
new file mode 100644
index 000000000..6dc20becd
--- /dev/null
+++ b/shenyu-sdk/shenyu-sdk-httpclient/src/main/java/org/apache/shenyu/sdk/httpclient/HttpClientShenyuHttpClient.java
@@ -0,0 +1,113 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shenyu.sdk.httpclient;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.http.Header;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.RequestBuilder;
+import org.apache.http.conn.HttpClientConnectionManager;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.util.EntityUtils;
+import org.apache.shenyu.sdk.core.ShenyuRequest;
+import org.apache.shenyu.sdk.core.ShenyuResponse;
+import org.apache.shenyu.sdk.core.http.ShenyuHttpClient;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.LinkedList;
+import java.util.stream.Collectors;
+
+/**
+ * shenyu httpclient.
+ */
+public class HttpClientShenyuHttpClient implements ShenyuHttpClient {
+
+    private final HttpClientConnectionManager connectionManager;
+
+    public HttpClientShenyuHttpClient(final HttpClientConnectionManager connectionManager) {
+        this.connectionManager = connectionManager;
+    }
+
+    private HttpClient getHttpClient() {
+        return HttpClients.custom().setConnectionManager(connectionManager).build();
+    }
+
+    @Override
+    public ShenyuResponse execute(final ShenyuRequest request) throws IOException {
+        String url = request.getUrl();
+        String body = request.getBody();
+        RequestBuilder requestBuilder;
+
+        switch (request.getHttpMethod()) {
+            case GET:
+                requestBuilder = RequestBuilder.get(url);
+                break;
+            case HEAD:
+                requestBuilder = RequestBuilder.head(url);
+                break;
+            case POST:
+                requestBuilder = RequestBuilder.post(url);
+                break;
+            case PUT:
+                requestBuilder = RequestBuilder.put(url);
+                break;
+            case DELETE:
+                requestBuilder = RequestBuilder.delete(url);
+                break;
+            case OPTIONS:
+                requestBuilder = RequestBuilder.options(url);
+                break;
+            case TRACE:
+                requestBuilder = RequestBuilder.trace(url);
+                break;
+            default:
+                requestBuilder = RequestBuilder.patch(url);
+                break;
+        }
+        if (StringUtils.isNotBlank(body)) {
+            requestBuilder.setEntity(createStringEntity(body));
+        }
+
+        Map<String, Collection<String>> headers = request.getHeaders();
+        for (String name : headers.keySet()) {
+            for (String value : headers.get(name)) {
+                requestBuilder.addHeader(name, value);
+            }
+        }
+
+        HttpResponse response = getHttpClient().execute(requestBuilder.build());
+        return new ShenyuResponse(response.getStatusLine().getStatusCode(), response.getStatusLine().getReasonPhrase(),
+                Arrays.stream(response.getAllHeaders()).collect(Collectors.groupingBy(Header::getName, HashMap::new,
+                        Collectors.mapping(Header::getValue, Collectors.toCollection(LinkedList::new)))),
+                EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8), request);
+    }
+
+    private StringEntity createStringEntity(final String body) {
+        StringEntity stringEntity = new StringEntity(body, StandardCharsets.UTF_8);
+        stringEntity.setContentType("application/json;charset=UTF-8");
+        return stringEntity;
+    }
+
+}
diff --git a/shenyu-sdk/shenyu-sdk-httpclient/src/test/java/org/apache/shenyu/sdk/httpclient/HttpClientShenyuHttpClientTest.java b/shenyu-sdk/shenyu-sdk-httpclient/src/test/java/org/apache/shenyu/sdk/httpclient/HttpClientShenyuHttpClientTest.java
new file mode 100644
index 000000000..6b732e8a0
--- /dev/null
+++ b/shenyu-sdk/shenyu-sdk-httpclient/src/test/java/org/apache/shenyu/sdk/httpclient/HttpClientShenyuHttpClientTest.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shenyu.sdk.httpclient;
+
+import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
+import org.apache.shenyu.sdk.core.ShenyuRequest;
+import org.apache.shenyu.sdk.core.ShenyuResponse;
+import org.junit.Test;
+import org.junit.jupiter.api.Assertions;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+public class HttpClientShenyuHttpClientTest {
+
+    @Test
+    public void testShenyuHttpClient() throws IOException {
+        HttpClientShenyuHttpClient shenyuHttpClient = new HttpClientShenyuHttpClient(new PoolingHttpClientConnectionManager());
+        Map<String, Collection<String>> headerMap = new HashMap<>();
+        headerMap.put("header", Arrays.asList("test1", "test2"));
+        ShenyuRequest shenyuRequest = ShenyuRequest.create(ShenyuRequest.HttpMethod.GET, "https://shenyu.apache.org",
+                headerMap, null, null);
+        ShenyuResponse response = shenyuHttpClient.execute(shenyuRequest);
+        Assertions.assertNotNull(response);
+    }
+
+}