commit 4320f572d5117cc85a30a220f8fba12303ab0811 Author: yaoh.wu Date: Tue Jul 30 17:01:58 2019 +0800 init diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1b4101c --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +*.iml +.idea/ +out/ +target/ \ No newline at end of file diff --git a/build.xml b/build.xml new file mode 100644 index 0000000..98259f0 --- /dev/null +++ b/build.xml @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/lib/.keep b/lib/.keep new file mode 100644 index 0000000..e69de29 diff --git a/lib/report/.gitkeep b/lib/report/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/plugin.xml b/plugin.xml new file mode 100644 index 0000000..5d37e5b --- /dev/null +++ b/plugin.xml @@ -0,0 +1,23 @@ + + + com.fr.plugin.cluster.req.demo + + yes + no + 1.0.0 + 10.0~ + 2019-07-30 + finereport.yaohwu + + + ]]> + + + + + + + + + diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..4f32948 --- /dev/null +++ b/pom.xml @@ -0,0 +1,92 @@ + + 4.0.0 + + com.fr.plugin + plugin-cluster-req-demo + 10.0-RELEASE-SNAPSHOT + pom + + UTF-8 + 1.8 + + + E:\FineCode\bugfix-core-10.0\webroot\WEB-INF\plugins\plugin-com.fr.plugin.cluster.req.demo-1.0.0 + + + + + + junit + junit + 4.12 + test + + + org.easymock + easymock + 3.5.1 + test + + + + + com.fr.decision + decision-feature + 10.0 + + + com.fr.report + engine + 10.0 + + + com.fr.decision + decision-report-feature + 10.0 + + + com.fr.webui + fine-webui + 10.0-RELEASE-SNAPSHOT + + + org.slf4j + slf4j-api + 1.7.7 + + + com.fr.third + fine-third + 10.0-RELEASE-SNAPSHOT + + + com.fr.third.server + servlet-api + 3.0 + + + com.fr.third.driver + ojdbc + 14 + + + + + ${plugin-web-inf-path}/classes + ${plugin-web-inf-path}/test-classes + + + maven-compiler-plugin + 3.1 + + 1.6 + 1.6 + none + + + + + \ No newline at end of file diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..ba04b1b --- /dev/null +++ b/readme.md @@ -0,0 +1,36 @@ +# ClusterForwardProvider 接口 demo + +demo 插件中提供了三个接口: + +1. /req/demo/create +2. /req/demo/search +3. /req/demo/view + +search 接口需要携带 create 接口返回的id; +携带了 id 参数的search 请求总是会被转发到生成该 id 的节点,即处理前一个 create 请求的节点上。 + +浏览器中访问 http://host:port/webroot/decision/url/req/demo/create 得到返回值,其中有 id: + +```json +{ + "id": "4da3ca4a-9bb9-5116-dbaf-3d005fc4cab9", + "isCluster": true, + "clusterId": "M+n28J6oNcJMRUJhC+UczHLb2t1xQWXk2jqx2HHpGu3smaPzPw6DDQ==", + "taskName": "4da3ca4a-9bb9-5116-dbaf-3d005fc4cab9M+n28J6oNcJMRUJhC+UczHLb2t1xQWXk2jqx2HHpGu3smaPzPw6DDQ==" +} +``` + +该请求被集群转发策略,例如随机转发或者智能转发,转发到了节点 M+n28J6oNcJMRUJhC+UczHLb2t1xQWXk2jqx2HHpGu3smaPzPw6DDQ== 上; +后续浏览器访问 http://host:port/webroot/decision/url/req/demo/search?id=4da3ca4a-9bb9-5116-dbaf-3d005fc4cab9 +的请求也会被转发到节点 M+n28J6oNcJMRUJhC+UczHLb2t1xQWXk2jqx2HHpGu3smaPzPw6DDQ==,返回值如下: + +```json +{ + "id": "4da3ca4a-9bb9-5116-dbaf-3d005fc4cab9", + "isCluster": true, + "clusterId": "M+n28J6oNcJMRUJhC+UczHLb2t1xQWXk2jqx2HHpGu3smaPzPw6DDQ==", + "taskName": "4da3ca4a-9bb9-5116-dbaf-3d005fc4cab9M+n28J6oNcJMRUJhC+UczHLb2t1xQWXk2jqx2HHpGu3smaPzPw6DDQ==" +} +``` + +而浏览器中访问 http://host:port/webroot/decision/url/req/demo/view 的请求会被代理例如 nginx 随机转发到某一节点。 diff --git a/src/main/java/com/fr/plugin/cluster/req/demo/ClusterLockManager.java b/src/main/java/com/fr/plugin/cluster/req/demo/ClusterLockManager.java new file mode 100644 index 0000000..dd42237 --- /dev/null +++ b/src/main/java/com/fr/plugin/cluster/req/demo/ClusterLockManager.java @@ -0,0 +1,20 @@ +package com.fr.plugin.cluster.req.demo; + +import com.fr.cluster.ClusterBridge; +import com.fr.cluster.lock.ClusterLock; +import com.fr.cluster.lock.ClusterLockFactory; + +public class ClusterLockManager { + private ClusterLockManager() { + } + + /** + * 获取锁 + * + * @return 集群锁 + */ + public static ClusterLock getLock() { + ClusterLockFactory factory = ClusterBridge.getLockFactory(); + return factory.get(ClusterLockManager.class); + } +} diff --git a/src/main/java/com/fr/plugin/cluster/req/demo/ReqDemoHandler.java b/src/main/java/com/fr/plugin/cluster/req/demo/ReqDemoHandler.java new file mode 100644 index 0000000..2f5729b --- /dev/null +++ b/src/main/java/com/fr/plugin/cluster/req/demo/ReqDemoHandler.java @@ -0,0 +1,34 @@ +package com.fr.plugin.cluster.req.demo; + +import com.fr.decision.fun.HttpHandler; +import com.fr.decision.fun.impl.AbstractAuthorityExpandTypesProvider; +import com.fr.decision.fun.impl.AbstractHttpHandlerProvider; +import com.fr.plugin.cluster.req.demo.handler.CreateAction; +import com.fr.plugin.cluster.req.demo.handler.SearchAction; +import com.fr.plugin.cluster.req.demo.handler.ViewAction; +import com.fr.stable.fun.mark.API; + +/** + * + */ +@API(level = AbstractAuthorityExpandTypesProvider.CURRENT_LEVEL) +public class ReqDemoHandler extends AbstractHttpHandlerProvider { + @Override + public HttpHandler[] registerHandlers() { + return new HttpHandler[]{ + new CreateAction(), + new SearchAction(), + new ViewAction() + }; + } + + @Override + public int currentAPILevel() { + return CURRENT_LEVEL; + } + + @Override + public String mark4Provider() { + return getClass().getName(); + } +} diff --git a/src/main/java/com/fr/plugin/cluster/req/demo/ReqDemoManager.java b/src/main/java/com/fr/plugin/cluster/req/demo/ReqDemoManager.java new file mode 100644 index 0000000..a6e7722 --- /dev/null +++ b/src/main/java/com/fr/plugin/cluster/req/demo/ReqDemoManager.java @@ -0,0 +1,73 @@ +package com.fr.plugin.cluster.req.demo; + +import com.fr.cluster.lock.ClusterLock; +import com.fr.plugin.cluster.req.demo.entity.Task; +import com.fr.plugin.transform.ExecuteFunctionRecord; +import com.fr.plugin.transform.FunctionRecorder; +import com.fr.stable.StringUtils; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +@FunctionRecorder +public class ReqDemoManager { + + + /** + * 本机所有处理中的任务 + */ + private static final Map PROCESSES = new ConcurrentHashMap(); + + private ReqDemoManager() { + } + + + private static ClusterLock getLock() { + return ClusterLockManager.getLock(); + } + + + @ExecuteFunctionRecord + public static void removeTask(String id) throws Exception { + ClusterLock lock = getLock(); + try { + lock.lock(); + if (id == null) { + return; + } + TaskInfoManager.removeTask(id); + PROCESSES.remove(id); + } finally { + lock.unlock(); + } + } + + + public static Task getTask(String id) throws Exception { + if (id == null) { + return null; + } + ClusterLock lock = getLock(); + try { + lock.lock(); + return PROCESSES.get(id); + } finally { + lock.unlock(); + } + } + + + public static void addTask(String id, Task task) throws Exception { + if (StringUtils.isBlank(id) || task == null) { + return; + } + ClusterLock lock = getLock(); + try { + lock.lock(); + TaskInfoManager.addTask(id); + PROCESSES.put(id, task); + } finally { + lock.unlock(); + } + } +} diff --git a/src/main/java/com/fr/plugin/cluster/req/demo/TaskInfoManager.java b/src/main/java/com/fr/plugin/cluster/req/demo/TaskInfoManager.java new file mode 100644 index 0000000..a8b7a52 --- /dev/null +++ b/src/main/java/com/fr/plugin/cluster/req/demo/TaskInfoManager.java @@ -0,0 +1,74 @@ +package com.fr.plugin.cluster.req.demo; + +import com.fr.cluster.lock.ClusterLock; +import com.fr.log.FineLoggerFactory; +import com.fr.store.StateHubManager; +import com.fr.store.StateHubService; +import com.fr.web.core.ClusterShareInfo; +import com.fr.web.core.cluster.ClusterStatusHelper; + +public class TaskInfoManager { + private static final String KEY = "ReqDemoTaskInfoPool"; + + private TaskInfoManager() { + } + + + /** + * 向队列中添加一个列表 + * + * @param id 任务id + * @throws Exception 异常 + */ + public static void addTask(String id) throws Exception { + ClusterLock lock = getLock(); + try { + lock.lock(); + StateHubService stateHubService = StateHubManager.applyForService(KEY); + stateHubService.put(id, new ClusterShareInfo(id, ClusterStatusHelper.getCurrentNodeId())); + } finally { + lock.unlock(); + } + } + + + public static void removeTask(String id) throws Exception { + ClusterLock lock = getLock(); + try { + lock.lock(); + StateHubService stateHubService = StateHubManager.applyForService(KEY); + stateHubService.delete(id); + } finally { + lock.unlock(); + } + } + + public static String getTargetNodeId(String id) { + ClusterLock lock = getLock(); + try { + lock.lock(); + StateHubService stateHubService = StateHubManager.applyForService(KEY); + ClusterShareInfo info = stateHubService.get(id); + if (info != null) { + return info.getNodeID(); + } else { + FineLoggerFactory.getLogger().debug("process cluster share info not found"); + return null; + } + } catch (Exception e) { + FineLoggerFactory.getLogger().debug("occur exception while finding process cluster share info", e); + return null; + } finally { + lock.unlock(); + } + } + + /** + * 获取锁(集群时就是集群锁,非集群时返回的是单机的) + * + * @return 集群锁 + */ + private static ClusterLock getLock() { + return ClusterLockManager.getLock(); + } +} diff --git a/src/main/java/com/fr/plugin/cluster/req/demo/URIConstants.java b/src/main/java/com/fr/plugin/cluster/req/demo/URIConstants.java new file mode 100644 index 0000000..f6d1a54 --- /dev/null +++ b/src/main/java/com/fr/plugin/cluster/req/demo/URIConstants.java @@ -0,0 +1,8 @@ +package com.fr.plugin.cluster.req.demo; + +public class URIConstants { + public static final String CREATE = "/req/demo/create"; + public static final String SEARCH = "/req/demo/search"; + public static final String VIEW = "/req/demo/view"; + +} diff --git a/src/main/java/com/fr/plugin/cluster/req/demo/cluster/ClusterReqDemoForwardProvider.java b/src/main/java/com/fr/plugin/cluster/req/demo/cluster/ClusterReqDemoForwardProvider.java new file mode 100644 index 0000000..4505b2c --- /dev/null +++ b/src/main/java/com/fr/plugin/cluster/req/demo/cluster/ClusterReqDemoForwardProvider.java @@ -0,0 +1,33 @@ +package com.fr.plugin.cluster.req.demo.cluster; + +import com.fr.plugin.cluster.req.demo.TaskInfoManager; +import com.fr.plugin.cluster.req.demo.URIConstants; +import com.fr.stable.fun.impl.AbstractClusterForwardProvider; +import com.fr.web.utils.WebUtils; +import org.jetbrains.annotations.Nullable; + +import javax.servlet.http.HttpServletRequest; + +public class ClusterReqDemoForwardProvider extends AbstractClusterForwardProvider { + @Override + public boolean needCreate(HttpServletRequest request) { + String requestUri = request.getRequestURI(); + return requestUri.contains(URIConstants.CREATE); + } + + @Override + public @Nullable String getTargetNodeID(String key) { + return TaskInfoManager.getTargetNodeId(key); + } + + @Override + public String getKey(HttpServletRequest request) { + return WebUtils.getHTTPRequestParameter(request, "id"); + } + + @Override + public boolean accept(HttpServletRequest request) { + String requestUri = request.getRequestURI(); + return requestUri.contains(URIConstants.CREATE) || requestUri.contains(URIConstants.SEARCH); + } +} diff --git a/src/main/java/com/fr/plugin/cluster/req/demo/entity/Task.java b/src/main/java/com/fr/plugin/cluster/req/demo/entity/Task.java new file mode 100644 index 0000000..825933e --- /dev/null +++ b/src/main/java/com/fr/plugin/cluster/req/demo/entity/Task.java @@ -0,0 +1,17 @@ +package com.fr.plugin.cluster.req.demo.entity; + +public class Task { + private String name; + + public Task(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/src/main/java/com/fr/plugin/cluster/req/demo/handler/CreateAction.java b/src/main/java/com/fr/plugin/cluster/req/demo/handler/CreateAction.java new file mode 100644 index 0000000..2e2bafe --- /dev/null +++ b/src/main/java/com/fr/plugin/cluster/req/demo/handler/CreateAction.java @@ -0,0 +1,52 @@ +package com.fr.plugin.cluster.req.demo.handler; + +import com.fr.decision.fun.impl.BaseHttpHandler; +import com.fr.json.JSONObject; +import com.fr.plugin.cluster.req.demo.ReqDemoManager; +import com.fr.plugin.cluster.req.demo.URIConstants; +import com.fr.plugin.cluster.req.demo.entity.Task; +import com.fr.third.jgroups.util.UUID; +import com.fr.third.springframework.web.bind.annotation.RequestMethod; +import com.fr.web.core.cluster.ClusterStatusHelper; +import com.fr.web.utils.WebUtils; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * + */ +public class CreateAction extends BaseHttpHandler { + + @Override + public RequestMethod getMethod() { + return RequestMethod.GET; + } + + @Override + public String getPath() { + return URIConstants.CREATE; + } + + @Override + public boolean isPublic() { + return true; + } + + @Override + public void handle(HttpServletRequest req, HttpServletResponse res) throws Exception { + JSONObject json = new JSONObject(); + String id = UUID.randomUUID().toString(); + json.put("id", id); + boolean isCluster = ClusterStatusHelper.isClusterEnv(); + json.put("isCluster", isCluster); + if (isCluster) { + String clusterNodeId = ClusterStatusHelper.getCurrentNodeId(); + json.put("clusterId", clusterNodeId); + String taskName = id + clusterNodeId; + ReqDemoManager.addTask(id, new Task(taskName)); + json.put("taskName", taskName); + } + WebUtils.printAsJSON(res, json); + } +} diff --git a/src/main/java/com/fr/plugin/cluster/req/demo/handler/SearchAction.java b/src/main/java/com/fr/plugin/cluster/req/demo/handler/SearchAction.java new file mode 100644 index 0000000..c033484 --- /dev/null +++ b/src/main/java/com/fr/plugin/cluster/req/demo/handler/SearchAction.java @@ -0,0 +1,52 @@ +package com.fr.plugin.cluster.req.demo.handler; + +import com.fr.decision.fun.impl.BaseHttpHandler; +import com.fr.json.JSONObject; +import com.fr.plugin.cluster.req.demo.ReqDemoManager; +import com.fr.plugin.cluster.req.demo.URIConstants; +import com.fr.plugin.cluster.req.demo.entity.Task; +import com.fr.third.springframework.web.bind.annotation.RequestMethod; +import com.fr.web.core.cluster.ClusterStatusHelper; +import com.fr.web.utils.WebUtils; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * + */ +public class SearchAction extends BaseHttpHandler { + @Override + public RequestMethod getMethod() { + return RequestMethod.GET; + } + + @Override + public String getPath() { + return URIConstants.SEARCH; + } + + @Override + public boolean isPublic() { + return true; + } + + @Override + public void handle(HttpServletRequest req, HttpServletResponse res) throws Exception { + JSONObject json = new JSONObject(); + String id = WebUtils.getHTTPRequestParameter(req, "id"); + json.put("id", id); + boolean isCluster = ClusterStatusHelper.isClusterEnv(); + json.put("isCluster", isCluster); + if (isCluster) { + String clusterNodeId = ClusterStatusHelper.getCurrentNodeId(); + json.put("clusterId", clusterNodeId); + Task task = ReqDemoManager.getTask(id); + if (task != null) { + json.put("taskName", task.getName()); + } + } + WebUtils.printAsJSON(res, json); + + } +} diff --git a/src/main/java/com/fr/plugin/cluster/req/demo/handler/ViewAction.java b/src/main/java/com/fr/plugin/cluster/req/demo/handler/ViewAction.java new file mode 100644 index 0000000..43e1e5f --- /dev/null +++ b/src/main/java/com/fr/plugin/cluster/req/demo/handler/ViewAction.java @@ -0,0 +1,43 @@ +package com.fr.plugin.cluster.req.demo.handler; + +import com.fr.decision.fun.impl.BaseHttpHandler; +import com.fr.json.JSONObject; +import com.fr.plugin.cluster.req.demo.URIConstants; +import com.fr.third.springframework.web.bind.annotation.RequestMethod; +import com.fr.web.core.cluster.ClusterStatusHelper; +import com.fr.web.utils.WebUtils; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * + */ +public class ViewAction extends BaseHttpHandler { + @Override + public RequestMethod getMethod() { + return RequestMethod.GET; + } + + @Override + public String getPath() { + return URIConstants.VIEW; + } + + @Override + public boolean isPublic() { + return true; + } + + @Override + public void handle(HttpServletRequest req, HttpServletResponse res) throws Exception { + JSONObject json = new JSONObject(); + boolean isCluster = ClusterStatusHelper.isClusterEnv(); + json.put("isCluster", isCluster); + if (isCluster) { + String clusterNodeId = ClusterStatusHelper.getCurrentNodeId(); + json.put("clusterId", clusterNodeId); + } + WebUtils.printAsJSON(res, json); + } +} \ No newline at end of file diff --git a/src/main/java/com/fr/plugin/cluster/req/demo/url/ReqDemoUrlProvider.java b/src/main/java/com/fr/plugin/cluster/req/demo/url/ReqDemoUrlProvider.java new file mode 100644 index 0000000..b0f355e --- /dev/null +++ b/src/main/java/com/fr/plugin/cluster/req/demo/url/ReqDemoUrlProvider.java @@ -0,0 +1,35 @@ +package com.fr.plugin.cluster.req.demo.url; + +import com.fr.decision.fun.impl.AbstractURLAliasProvider; +import com.fr.decision.webservice.url.alias.URLAlias; +import com.fr.decision.webservice.url.alias.URLAliasFactory; +import com.fr.stable.fun.mark.API; + +import static com.fr.plugin.cluster.req.demo.URIConstants.CREATE; +import static com.fr.plugin.cluster.req.demo.URIConstants.SEARCH; +import static com.fr.plugin.cluster.req.demo.URIConstants.VIEW; + +/** + * + */ +@API(level = AbstractURLAliasProvider.CURRENT_LEVEL) +public class ReqDemoUrlProvider extends AbstractURLAliasProvider { + @Override + public URLAlias[] registerAlias() { + return new URLAlias[]{ + URLAliasFactory.createPluginAlias(CREATE, CREATE, true), + URLAliasFactory.createPluginAlias(VIEW, VIEW, true), + URLAliasFactory.createPluginAlias(SEARCH, SEARCH, true), + }; + } + + @Override + public int currentAPILevel() { + return CURRENT_LEVEL; + } + + @Override + public String mark4Provider() { + return getClass().getName(); + } +}