2009-07-14 26 views
8

Tôi tự hỏi làm cách nào mọi người quản lý việc duy trì tài nguyên JNDI trên nhiều phiên bản của máy chủ ứng dụng Tomcat của họ. Cho phép lấy, ví dụ, tài nguyên cơ sở dữ liệu JNDI của tôi. Nó được khai báo trong tệp /conf/context.xml của tôi và các tham chiếu từ tệp web.xml ứng dụng của tôi.Duy trì JNDI trên nhiều trường hợp của Tomcat

Tài nguyên JNDI đó phải được định nghĩa độc lập trên hộp phát triển của tôi, hộp dàn và hộp sản xuất. Điều gì sẽ xảy ra nếu tôi muốn thiết lập một phiên bản mới của hộp phát triển/dàn dựng/sản xuất? Điều đó có nghĩa là tôi phải tạo lại tên tài nguyên bên trong context.xml cho mỗi cá thể mới mà tôi mang đến? Từ quan điểm thiết lập, đây là nơi có thể có một số lỗi của con người có thể khiến máy chủ ứng dụng bắt đầu trỏ đến DB sai.

Tôi thấy điều này rất cồng kềnh và khó hiểu khi tôi mở rộng cả số lượng nhà phát triển trên dự án của mình cũng như số lượng máy chủ sản xuất mà tôi có thể sử dụng.

Bạn chỉ cần làm cho nó trở thành một phần trong quá trình thiết lập của bạn hoặc tạo tập lệnh thiết lập liên quan đến điều này cho bạn mỗi khi bạn cài đặt lại tomcat và thiết lập hộp? Hay liệu có một số mức độ khác biệt nào có thể làm cho điều này dễ dàng hơn không?

Trả lời

1

Bạn đang triển khai nhiều ứng dụng web phải sử dụng chia sẻ tài nguyên?

Nếu không, hoàn toàn KHÔNG có lý do nào để khai báo tài nguyên của bạn trong /conf/context.xml. Thay vào đó, chúng nên được khai báo trong một tệp riêng biệt, riêng tư với tệp context.xml ứng dụng web của bạn sẽ được triển khai dưới dạng /META-INF/context.xml bên trong WAR của bạn. Tệp đó, cùng với tệp web.xml của bạn nên được kiểm tra trong hệ thống kiểm soát nguồn của bạn và được triển khai như là một phần của bản dựng của bạn - không có sự can thiệp thủ công nào.

Nếu bạn đang triển khai nhiều ứng dụng web với tài nguyên được chia sẻ, bạn có thể viết nhà máy tài nguyên tùy chỉnh để hiển thị cùng một tài nguyên cho nhiều ứng dụng web (xem Tomcat documentation, ở cuối trang) và sử dụng phương pháp trên hoặc - môi trường phát triển ít nhất - bạn có thể tự động thay đổi (hoặc thậm chí thay thế như mặc định không có gì) /conf/context.xml như một phần của bản dựng của bạn. Đối với triển khai sản xuất không được khuyến khích, tất nhiên.

+1

Những phiếu giảm giá bằng lái xe đó thực sự gây phiền toái. Nếu bạn đang bỏ phiếu cho một người nào đó, hãy can đảm và để lại một bình luận tại sao. Điều đó tăng gấp đôi khi bạn nghĩ rằng tôi đã nói điều gì sai trong câu trả lời của tôi - làm cách nào khác mà mọi người sẽ học? – ChssPly76

+4

Tôi không bỏ phiếu nhưng tôi không đồng ý với bạn. Các tài nguyên như nguồn dữ liệu JDBC và phiên thư không bao giờ được xác định trong chính bản thân WAR. Các tham số kết nối cơ sở dữ liệu là các thuộc tính của môi trường, không phải của ứng dụng. Người ta có thể triển khai WAR trong môi trường thử nghiệm trước khi nó đi vào sản xuất. –

+0

Tôi đồng ý với Maurice nhưng tôi nghĩ rằng ChssPly76 đáng để bỏ phiếu vì anh ta đang đưa ra câu trả lời cho câu hỏi. IIRC Bea Weblogic có một "proxy JNDI" cho phép tạo một tham chiếu JNDI trỏ đến tài nguyên JNDI khác – ATorras

0

Điểm của JNDI là phải có tài nguyên cụ thể về môi trường được xác định độc lập. Môi trường phát triển, dàn dựng và sản xuất của bạn không được chia sẻ cùng một cơ sở dữ liệu (hoặc ở bất kỳ mức nào, JNDI được thiết kế để cho phép các cơ sở dữ liệu riêng biệt cho từng môi trường).

Nếu mặt khác bạn đang cố tải cân bằng nhiều máy chủ Tomcat và bạn muốn tất cả các phiên bản chia sẻ cùng một cấu hình, tôi nghĩ bạn luôn có thể chia nhỏ tệp ngữ cảnh của mình và có các bit chung trong một tệp được chia sẻ. Đây là Tomcat documentation talking about context.xml.

Cách bạn quản lý các tùy chọn này tùy thuộc vào bạn. Nó có thể đơn giản, giống như có một tệp mẫu "template" context.xml mà bạn bắt đầu với mỗi khi bạn tạo một cá thể Tomcat mới (có những điều này trong hệ thống kiểm soát nguồn, đặc biệt là một hệ thống phân phối, có thể hữu ích). Hoặc bạn có thể viết một kịch bản để làm điều này.

Nếu bạn muốn nhiều hơn, tôi tin rằng có những sản phẩm ngoài kia giúp đặt giao diện người dùng tốt đẹp xung quanh toàn bộ quá trình. Một điều tôi tin là đây là SpringSource tc Server.

+0

Tôi sẽ không chia sẻ cùng một DB giữa tất cả các máy chủ khác nhau. Làm thế nào bạn có nghĩa vụ phải chia sẻ một context.xml phổ biến trong số các hộp máy chủ khác nhau? Ngoài ra nếu thực sự có vẻ như tôi phải duy trì context.xml khác nhau cho mỗi hộp những gì tốt quy trình/thực hành là có cho tự động hóa nhiệm vụ này và giảm lỗi của con người? – Ish

+0

Tôi không nghĩ rằng bạn thực sự có thể chia sẻ tệp giữa các hộp khác nhau mà không cần sử dụng các hệ thống tệp được chia sẻ (ví dụ: NFS có liên kết tượng trưng). Tôi cũng đã cập nhật câu trả lời để tham khảo tài liệu Tomcat nói về cấu hình context.xml. –

3

Tôi giả định rằng đối với một tài nguyên nhất định, bạn sử dụng cùng một tên JNDI trong mỗi môi trường. Nếu không, bạn phải chỉnh sửa mã của mình để trỏ đến tên tài nguyên mới (JNDI).

Thiết lập môi trường lần đầu tiên gần như không thể kiểm tra trước thời hạn.Không có cách nào để xác minh rằng một số chuỗi, giống như chuỗi kết nối cơ sở dữ liệu sản xuất, đã không nhận được chất béo ngón tay cho đến khi bạn thực sự phải sử dụng nó. Đó là bản chất của cấu hình môi trường. Với điều đó đã nói, nếu bạn muốn giảm bớt khả năng mắc lỗi, trước tiên bạn cần đảm bảo rằng mỗi tài nguyên được cung cấp một tên được sử dụng bất kể môi trường nào được lưu trữ. Tạo một tên tài nguyên dbConnectionString trong một tệp thuộc tính trỏ tới jndi:/jdbc/myproject/resources/dbConnectionString và đảm bảo tất cả các môi trường đều khai báo cùng một tài nguyên đó. Dưới đây là cách chúng tôi giữ mã được phân lập từ các loại phụ thuộc môi trường này. Điều đó đang được nói, bạn sẽ luôn phải tự xác minh rằng cấu hình của một máy chủ cụ thể đang sử dụng các giá trị thích hợp cho các tài nguyên được xác định.

LƯU Ý: bao giờ tạo tên tài nguyên như "dbProdConnectionString", "dbQAConnectionString", "dbDevConnectionString". Bạn sẽ đánh bại mục đích cố gắng loại bỏ can thiệp thủ công vì sau đó bạn đã thêm một bước gián tiếp cần thay đổi mã (để trỏ mã vào tên tài nguyên chính xác) và xây dựng (để gói các thay đổi vào tệp .war của bạn) khi di chuyển giữa các môi trường.


Chúng tôi đã tạo cấu trúc thư mục cho các thuộc tính cụ thể về môi trường. Trong thư mục đó, chúng tôi đã tạo các thư mục cho từng môi trường cụ thể được nhắm mục tiêu để triển khai, bao gồm cả phát triển cục bộ. Nó trông như thế này:

Project 
\ 
-Properties 
\ 
    -Local (your PC) 
    -Dev (shared dev server) 
    -Test (pre-production) 
    -Prod (Production) 

Trong mỗi thư mục chúng tôi đặt các bản sao song song của các tệp thuộc tính/cấu hình và chỉ đặt các cấu hình khác nhau trong tệp trong thư mục thích hợp. Bí mật là để kiểm soát classpath của môi trường triển khai. Chúng tôi đã xác định mục nhập classpath PROPERTIES trên mỗi máy chủ. Trên Prod, nó sẽ được đặt thành "$ ProjectDir/Properties/Prod" trong khi kiểm tra cùng một biến sẽ được đặt thành "$ ProjectDir/Properties/Test". Bằng cách này chúng ta có thể có các chuỗi kết nối cơ sở dữ liệu cho cơ sở dữ liệu dev/test/prod được cấu hình sẵn và không phải kiểm tra/trong tệp thuộc tính mỗi khi chúng ta muốn xây dựng cho một môi trường khác. Quay lại đầu trang ||||

Điều này cũng có nghĩa là chúng tôi có thể triển khai chính xác cùng một tệp .war/.ear để Kiểm tra và Prod mà không cần xây dựng lại. Bất kỳ thuộc tính nào không được khai báo trong tệp thuộc tính mà chúng ta đã xử lý theo cách tương tự bằng cách sử dụng cùng tên JNDI trong mỗi môi trường nhưng sử dụng các giá trị cụ thể cho môi trường đó.

0

Giải pháp của tôi là đặt tất cả các định nghĩa thành tệp server-template.xml và sau đó sử dụng biến đổi XSLT thông minh để tạo ra server.xml cuối cùng cho mỗi trường hợp. Tôi đang sử dụng một tập tin kiến ​​xây dựng để sao chép tất cả các tập tin cho một cài đặt Tomcat và để cho nó tạo ra một server.xml từ mẫu. Tất cả mọi thứ được lưu trong CVS, vì vậy tôi có thể nhanh chóng khôi phục cài đặt mà không cần phải lo lắng rằng một cái gì đó có thể không hoạt động. Đây là mẫu trông giống như sau:

<Server port="${tomcat.server.port}" shutdown="SHUTDOWN" 
    xmlns:x="http://my.company.com/tomcat-template"> 

    <x:define name="Derby-DataSource" username="???" password="???" url="???" 
     auth="Container" type="javax.sql.DataSource" 
     maxActive="50" maxIdle="5" maxWait="300" 
     driverClassName="org.apache.derby.jdbc.ClientDriver" 
     testWhileIdle="true" timeBetweenEvictionRunsMillis="3600000" 
     removeAbandoned="true" removeAbandonedTimeout="60" logAbandoned="true" /> 
    <x:define x:element="Resource" name="Derby/Embedded/TDB" auth="Container" type="javax.sql.DataSource" 
     maxActive="50" maxIdle="5" maxWait="300" 
     username="" password="" driverClassName="org.apache.derby.jdbc.EmbeddedDriver" 
     url="jdbc:derby:D:/tmp/TestDB" /> 
    <x:define x:element="Resource" name="Derby/TDB" auth="Container" type="javax.sql.DataSource" 
     maxActive="50" maxIdle="5" maxWait="300" 
     username="junit" password="test" driverClassName="org.apache.derby.jdbc.ClientDriver" 
     url="jdbc:derby://localhost:1527/TDB" /> 
    <x:define x:element="Resource" name="Derby/P6/TDB" auth="Container" type="javax.sql.DataSource" 
     maxActive="50" maxIdle="5" maxWait="300" 
     username="junit" password="test" driverClassName="com.p6spy.engine.spy.P6SpyDriver" 
     url="jdbc:derby://localhost:1527/TDB" /> 

    ... lots of Tomcat stuff ... 

    <!-- Global JNDI resources --> 
    <GlobalNamingResources> 
    <x:if server="local"> 
     <!-- Local with Derby Network Server --> 
     <x:use name="Derby/TDB"><x:override name="jdbc/DB" maxIdle="1" /></x:use> 
     <x:use name="Derby/TDB"><x:override name="jdbc/DB_APP" maxIdle="1" /></x:use> 
     <x:use name="Derby/TDB"><x:override name="jdbc/DB2" maxIdle="1" /></x:use> 
    </x:if> 

    <x:if env="test"> ... same for test </x:if> 
    <x:if env="prod"> ... same for test </x:if> 
    </GlobalNamingResources> 
</Server> 

Như bạn có thể thấy, tôi xác định mặc định và sau đó chuyên cài đặt. Trong môi trường, sau đó tôi ghi đè lên một số thứ (hệ thống cục bộ nhận được một hồ bơi nhỏ hơn so với thử nghiệm sản xuất và tích hợp).

Bộ lọc kịch bản trông như thế này:

<!-- 

This XSLT Stylesheet transforms the file server-template.xml into server-new.xml. 

It will perform the following operations: 

- All x:define elements are removed 
- All x:use elements will be replaces with the content and attributes 
    of the respective x:define element. The name of the new element is 
    specified with the attribute "x:element". 
- All attributes in the x:override elements will overwrite the respective 
    attributes from the x:define element. 
- x:if allows to suppress certain parts of the file altogether. 

Example: 

    <x:define element="Resource" name="Derby/Embedded/TDB" auth="Container" ... /> 
    <x:use name="Derby/Embedded/TDB"><x:override name="NewTDB" /></x:use> 

becomes: 

    <Resource name="NewTDB" auth="Container" ... /> 

i.e. the attribute x:element="Resource" in x:define becomes the name of the 
new element, name="Derby/Embedded/ABSTDB" in x:use specifies which x:define 
to use and name="NewTDB" in x:override replaces the value of the "name" 
attribute in x:define. 
--> 


<xsl:stylesheet version="1.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns:x="http://my.company.com/tomcat-template"> 
<xsl:output method="xml"/> 
<!-- Key for fast lookup of x:defines --> 
<xsl:key name="def" match="//x:define" use="@name" /> 
<!-- Parameters which can be used in x:if --> 
<xsl:param name="server" /> 
<xsl:param name="env" />  
<xsl:param name="instance" /> 

<!-- Copy everything by default --> 
<xsl:template match="node()|@*"> 
    <xsl:copy><xsl:apply-templates select="node()|@*"/></xsl:copy> 
</xsl:template> 

<!-- filter x:defines --> 
<xsl:template match="x:define"></xsl:template> 

<!-- x:use is replaced by the matching x:define --> 
<xsl:template match="x:use"> 
    <!-- Find the x:define --> 
    <xsl:variable name="def" select="key('def', @name)" /> 
    <!-- Save the x:use node in a variable --> 
    <xsl:variable name="node" select="." /> 

    <!-- Start a new element. the name is in the attribute x:element of the x:define --> 
    <xsl:element name="{$def/@x:element}"> 
    <!-- Process all attributes in the x: namespace --> 
    <xsl:for-each select="$def/@x:*"> 
     <xsl:choose> 
     <xsl:when test="name() = 'x:extends'"> 
      <xsl:variable name="extName" select="." /> 
      <xsl:variable name="ext" select="key('def', $extName)" /> 
      <xsl:for-each select="$ext/@*"> 
      <xsl:if test="namespace-uri() != 'http://my.company.com/tomcat-template'"> 
       <xsl:copy /> 
      </xsl:if> 
      </xsl:for-each> 
     </xsl:when> 
     </xsl:choose> 
    </xsl:for-each> 

    <!-- Copy all attributes from the x:define (except those in the x: namespace) --> 
    <xsl:for-each select="$def/@*"> 
     <xsl:if test="namespace-uri() != 'http://my.company.com/tomcat-template'"> 
     <xsl:copy /> 
     </xsl:if> 
    </xsl:for-each> 

    <!-- If there is an x:override-Element, copy those attributes. This 
     will overwrite existing attributes with the same name. --> 
    <xsl:for-each select="$node/x:override/@*"> 
     <xsl:variable name="name" select="name()" /> 
     <xsl:variable name="value" select="." /> 
     <xsl:variable name="orig" select="$def/attribute::*[name() = $name]" /> 

     <xsl:choose> 
     <!-- ${orig} allows to acces the attributes from the x:define --> 
     <xsl:when test="contains($value, '${orig}')"> 
      <xsl:attribute name="{$name}"><xsl:value-of select="substring-before($value, '${orig}')" 
      /><xsl:value-of select="$orig" 
      /><xsl:value-of select="substring-after($value, '${orig}')" 
      /></xsl:attribute> 
     </xsl:when> 
     <xsl:otherwise> 
      <xsl:copy /> 
     </xsl:otherwise> 
     </xsl:choose> 
    </xsl:for-each> 
    <!-- Copy all child nodes, too --> 
    <xsl:apply-templates select="$def/node()"/> 
    </xsl:element> 
</xsl:template> 

<!-- x:if, to filter parts of the document --> 
<xsl:template match="x:if"> 
    <!-- t will be non-empty if any of the conditions matches --> 
    <xsl:variable name="t"> 
    <!-- Check for each paramater whether it is used in the x:if. If so, 
     check the value. If the value is the same as the stylesheet 
     paramater, the condition is met. Missing conditions count 
     as met, too. 
    <xsl:if test="not(@server) or @server = $server">1</xsl:if> 
    <xsl:if test="not(@env) or @env = $env">1</xsl:if> 
    <xsl:if test="not(@instance) or @instance = $instance">1</xsl:if> 
    </xsl:variable> 
    <xsl:if test="normalize-space($t) = '111'"> 
    <xsl:comment> <xsl:value-of select="$server" />, Env <xsl:value-of select="$env" />, Instance <xsl:value-of select="$instance" /> </xsl:comment> 
    <xsl:apply-templates select="node()|@*"/> 
    </xsl:if> 
</xsl:template> 

</xsl:stylesheet> 
1

Nếu bạn đang sử dụng khuôn khổ mùa xuân, bạn có thể giải quyết vấn đề này rất dễ dàng bằng cách sử dụng PropertyPlaceholderConfigurer. Lớp này cho phép bạn di chuyển các định nghĩa vào các tệp thuộc tính bên ngoài.Cấu hình nguồn dữ liệu như sau:

<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> 
<property name="driverClassName"><value>${jdbc.driver}</value></property> 
<property name="url"><value>${jdbc.url}</value></property> 
<property name="username"><value>${jdbc.user}</value></property> 
<property name="password"><value>${jdbc.password}</value></property> 
</bean> 

Các thuộc tính tự được định nghĩa trong một thuộc tính tiêu chuẩn file:

jdbc.driverClassName=com.mysql.jdbc.Driver 
jdbc.url=jdbc:mysql://host/database 
jdbc.username=user 
jdbc.password=secret 

Đối với tài sản thực tế mà bạn có hai lựa chọn:

  1. Đặt tệp thuộc tính bên ngoài trên hệ thống tệp, vì vậy nó sẽ khác nhau trên mọi máy:

    <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> 
        <property name="location">file:/etc/yourapp/jdbc.properties</property> 
        <!-- on windows, put the file in c:\etc\yourapp, the definition will work --> 
    </bean> 
    
  2. Thêm thuộc tính hệ thống sau vào máy chủ của bạn -Denv = [development | test | production]. Sau đó, đặt ba tệp cấu hình trong thư mục WEB-INF/classes của bạn: jdbc-development.properties, test-development.properties và production-development.properties. Cấu hình bối cảnh sẽ là:

    <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> 
        <property name="location">classpath:jdbc-${env}.properties</property> 
    </bean> 
    
Các vấn đề liên quan