【问题标题】:What would be a better design to resolve host/port lookups解决主机/端口查找的更好设计是什么
【发布时间】:2011-07-18 01:46:09
【问题描述】:
static image (e.g. car.png)
<table><tr><td><img src="http://<somehost>:<someport>/images/car.png" /></td></tr></table>

(e.g. lookup by id=123456 and fetched via a servlet from the database)
<table><tr><td><img src="http://<somehost>:<someport>/doc?id=123456"/></td></tr></table>

我们生成 HTML 代码的 sn-ps(如上所述)并将它们存储在用于以动态方式重新构建用户特定页面的数据库中。

上述场景中的问题是 somehost / someport 是静态绑定并存储在数据库中的,我想避免这种情况,因为如果我必须升级到具有不同 IP 的不同机器,所有上述调用都会失败。

如何以通用方式解决此问题,以便我可以在稍后阶段绑定主机/端口。

【问题讨论】:

    标签: java html web-applications servlets


    【解决方案1】:

    你为什么不用这种方式?这应该工作

    static image (e.g. car.png)
    <table><tr><td><img src="images/car.png" /></td></tr></table>
    
    (e.g. lookup by id=123456 and fetched via a servlet from the database)
    <table><tr><td><img src="/doc?id=123456"/></td></tr></table>
    

    【讨论】:

    • 不,以上假设它正在由现有容器上的 servlet 处理。我需要灵活地将其指向不同的服务器以获得高可扩展性。
    • @user339108 您可以在服务器上添加重写规则以反弹到正确的主机。
    • 为了获得高可扩展性,请将服务器置于负载均衡器之后,这样您就不必在意哪个服务器被击中了。如果您将信息存储在 Session 中,请停止这样做。
    【解决方案2】:

    你可以试试

    <img src="http://<%=request.getServerName()%>:request.getServerPort()/images/car.png" />
    

    查看 Java docs
    或者,您可以尝试

    <% @page import="java.net.InetAddress" %>
    <%
    InetAddress ia = InetAddress.getLocalHost();
    String hostName = ia.getHostName();
    %>
    

    【讨论】:

    • HTML 保存在 DB 中。在 JSP 中打印上述 HTML 时,您将如何评估那些(丑陋的)scriptlet 表达式?这种方式肯定行不通。此外,InetAddress 如果返回 localhost,则永远不会工作。
    【解决方案3】:

    首先,将 HTML 存储在数据库中并不是一个好主意。但是,唉。

    至于具体问题,你可以只定义一个 HTML &lt;base&gt; 标签,它会使文档中的所有相对 URL 都成为它的相对 URL。

    <%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
    ...
    <head>
        <c:set var="r" value="${pageContext.request}" />
        <base href="${fn:replace(r.requestURL, r.requestURI, '')}${r.contextPath}/" />
    </head>
    

    这样你就可以使用

    <table><tr><td><img src="images/car.png" /></td></tr></table>
    <table><tr><td><img src="doc?id=123456"/></td></tr></table>
    

    如果没有基础,您将依赖于上下文路径。


    如果你真的想要参数化它们,那么我会使用java.text.MessageFormat。您可以使用{0}{1}{2} 等作为第一个、第二个、第三个等参数的占位符。

    <table><tr><td><img src="{0}/images/car.png" /></td></tr></table>
    <table><tr><td><img src="{0}/doc?id=123456"/></td></tr></table>
    

    您可以从HttpServletRequest 获取当前主机/端口(和上下文!),如下所示:

    HttpServletRequest r = getItSomehow();
    String base = r.getRequestURL().toString().replace(r.getRequestURI(), "") + r.getContextPath();
    

    您可以按如下方式从 DB 中格式化 HTML:

    String html = getItSomehow();
    String formatted = MessageFormat.format(html, base);
    

    然后在 JSP 中显示。您甚至可以将其包装在自定义 EL 函数中。更重要的是,像 JSF 这样的一些 MVC 框架也有使用 MessageFormat 的标签。例如

    <h:outputFormat value="#{bean.html}" escape="false">
        <f:param value="#{bean.base}" />
    </h:outputFormat>
    

    【讨论】:

      【解决方案4】:

      基本问题是您的数据库中有特定的文本,您需要能够在某个时间点替换其中的某些部分。 除了占位符之外,您别无选择。

      你现在可以做两件事:

      a) 使用占位符将您的 html 存储在数据库中:

      <table><tr><td><img src="http://%1:%2/images/car.png" /></td></tr></table>
      

      并使用

      替换占位符
      String.format(strHtmlFromDatabase, strHost, strPort);
      

      这样做的缺点是您每次都替换字符串(在每次请求时)。所以你应该把它缓存在内存中,或者:

      b) 执行与 a) 相同的操作,并使用第二个数据库表甚至文件来存储 html 字符串的“最终”(完全替换)版本。然后编写一个小型生成器脚本,该脚本从 a) 中获取 spaceholder-version,填充值,然后将其存储在 b) 中的表/文件中。然后,您的应用程序需要使用此表/文件中的最终字符串。您只需在每次主机和端口更改时运行此脚本。

      【讨论】:

        【解决方案5】:

        您将需要重构优化以在不使用廉价黑客的情况下实现目标。

        您应该将 HTML 生成器的输入存储在数据库中,而不是输出,从数据库传输文本几乎不会比在应用程序服务器端生成文本更有效。事实上,所有的数据库访问都很糟糕,应该避免。

        而是在应用服务器端创建一个合适的缓存。为获得页面组合的最大性能,请将常用文本元素减少为字节数组(如果需要,可以使用 XML 序列化程序)并使用每线程编写器来应用“somehost”、“someport”和“id”。

        如果不需要数据库访问,这将为您每秒提供数百次点击。如果您设法很好地构建和填充缓存(分而治之),通过散列您的输入请求,如果缓存被命中,您可能会在普通服务器上每秒获得超过 2000 次命中。

        使用 Varnish 等缓存进一步优化。

        【讨论】:

          【解决方案6】:

          有几种方法可以实现这一点,这取决于需要从哪种 HTML 中提取这些 URI。如果您只处理图像、链接、样式表和其他 HTML 特定导入,那么您可以尝试基于 DNS 重定向的方法。我将假设您可以访问一些额外的资源,特别是 Web 服务器或 servlet 容器,并且能够控制该服务器的 DNS。

          所以首先在创建时解析您的文档以提取所有引用的主机名(或者如果它只是特定的主机名,则查找它们)。然后使用这些主机名(和端口,如果指定)作为数据库查找的键。将您的主机名转换为唯一代码。如果您没有找到该主机名的条目,请创建一个并添加它。基本上将您的主机名转换为代表代码。现在介绍一些 DNS 技巧。获取您的 unqiuely 生成的代码并将其附加到一些已设置为指向您上述 Web 服务器的 DNS。为此,您需要设置DNS wildcards。所以举个例子。说明您的源内容是否如下所示:

          <p>Why is it that all my baking ends up on <a href="http://cakewrecks.blogspot.com/p/faq.html">the internet</a></p>
          

          您提取主机名cakewrecks.blogspot.com。在转换表中查找它并将其转换为代表代码,例如11ag3。然后将其附加到 Web 服务器主机名,例如11ag3.content.mycompany.com

          现在您使用新主机名并将其重新插入到您的内容中。

          <p>Why is it that all my baking ends up on <a href="http://11ag3.content.mycompany.com/p/faq.html">the internet</a></p>
          

          接下来,您需要编写一个 servlet(或者可能是其他一些动态代码)。这应该做的是拦截任何传入的 HTTP 请求(来自您的内容的所有请求现在都应该在这个 servlet 上结束,而不是它们最初去的地方)。所以 servlet 接收到一个对

          的请求
          http://11ag3.content.mycompany.com/p/faq.html
          

          它提取主机名11ag3.content.mycompany.com,并从中提取唯一代码11ag3。它现在与我们在创建文档时所做的相反,并查找原始主机名,并将其放回请求中,从而重构http://cakewrecks.blogspot.com/p/faq.html。它现在使用 HTTP 300 状态代码(可能是 307,临时重定向)以及重构的 URL 响应请求。

          这里最大的优势是您现在拥有一个包含所有主机名的数据库表。如果您想更改提供某些内容的位置,只需使用新主机名更新相应的条目即可。此外,您可以避免每次为每个页面提供模板时的开销。

          这种方法的主要问题可能是闪存,它具有复杂的安全模型。不过,您也许可以通过深入了解 cross domain policies 的复杂世界来使其正常工作。

          【讨论】:

            【解决方案7】:

            这个解决方案实际上是其他人都评论过的,但这是一种更优雅的替换方式,也是我在应用程序中使用的方式。

            您的数据库中存储的参数几乎都是动态的。例如,即使您处于开发模式与生产模式,我也可以看到 [somehost] 发生变化。我为解决这个问题而创建的是一个为我大量替换变量的类,我称这个类为 Replacer(代码如下)。

            我建议您将 [somehost] 和所有 [...] 参数作为参数存储在数据库中,例如:[x]、[y]、[z]。如果您可以将 x、yz 放在一个枚举中并将它们的序号保存在数据库中,例如数据库中的 [1]、[2]、[3],那么您将在 db 驱动器上节省大量空间。

            稍后创建一个 Properties 对象,该对象静态创建一个填充静态替换器,并对来自数据库的所有数据运行替换器。

            public class Replacer {
            
            private final Map<String, Object> replacements = new HashMap<String, Object>();
            
            public Replacer () {
            
                replacements.put("''", "\"");
            
            }
            
            public void addReplacement (String replaceWhat, Object replaceWith) {
            
                replacements.put(replaceWhat, replaceWith);
            
            }
            
            public String replace ( Object contentToReplace ) {
            
                String output = contentToReplace.toString();
            
                for (String replacement : replacements.keySet() ) {
            
                    output = output.replace(replacement, replacements.get(replacement).toString() );
            
                }
            
                return output;
            
            }
            
            public static void main (String[] args) throws Exception {
            
                testReplaceTwoSingleQuote();
            
            }
            
            public static void testReplaceTwoSingleQuote () throws Exception {
            
                Replacer rep = new Replacer();
            
                assert rep.replace( "And Mary said, ''Hello Bob''. ").equals("And Mary said, \"Hello Bob\".");
            
            }
            

            }

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2011-02-04
              • 2010-12-29
              • 2011-08-14
              • 2012-08-05
              • 2010-09-15
              • 2022-06-28
              • 2023-03-22
              • 1970-01-01
              相关资源
              最近更新 更多