英文:
How to change ActiveMQ KahaDB directory in TomEE
问题
从TomEE 9控制台上,我可以看到ActiveMQ KahaDB正在使用C:\Windows\system32\activemq-data\localhost\KahaDB
作为其默认存储目录。
Jun 29, 2023 10:00:01 AM org.apache.activemq.broker.BrokerService <clinit>
INFO: Loaded the Bouncy Castle security provider at position: 14
Jun 29, 2023 10:00:02 AM org.apache.activemq.broker.BrokerService
doStartPersistenceAdapter
INFO: Using Persistence Adapter:
KahaDBPersistenceAdapter[C:\Windows\system32\activemq-data\localhost\KahaDB]
Jun 29, 2023 10:00:02 AM org.apache.activemq.store.SharedFileLocker doStart
INFO: Database activemq-data\localhost\KahaDB\lock is locked by another server. This broker is now in slave mode waiting a lock to be acquired
由于C:\Windows\system32
是一个受保护的文件夹,它导致ActiveMQ生成以下INFO消息:
INFO: Database activemq-data\localhost\KahaDB\lock is locked by another server. This broker is now in slave mode waiting a lock to be acquired
我找到了这个链接,其中记录了如何自定义配置KahaDB。配置看起来足够简单,但我不知道应该将配置放在哪个XML文件中?我尝试过server.xml
,但没有成功。
以下是server.xml
的内容:
<?xml version="1.0" encoding="UTF-8"?>
<!--
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.
--><!-- 注意:"Server"本身不是"Container",因此您不能在此级别定义子组件,例如"Valves"。
文档位于/docs/config/server.html
-->
<Server port="18005" shutdown="SHUTDOWN">
<!-- TomEE插件适用于Tomcat -->
<Listener className="org.apache.tomee.catalina.ServerListener"/>
<Listener className="org.apache.catalina.startup.VersionLoggerListener"/>
<!-- 安全监听器。文档位于/docs/config/listeners.html
<Listener className="org.apache.catalina.security.SecurityListener" />
-->
<!-- APR库加载程序。文档位于/docs/apr.html -->
<Listener SSLEngine="on" className="org.apache.catalina.core.AprLifecycleListener"/>
<!-- 防止使用特定java/javax API导致内存泄漏-->
<Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener"/>
<Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener"/>
<Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener"/>
<!-- 全局JNDI资源
文档位于/docs/jndi-resources-howto.html
-->
<GlobalNamingResources>
<!-- 可编辑的用户数据库,也可用于
UserDatabaseRealm以验证用户
-->
<Resource auth="Container" description="User database that can be updated and saved" factory="org.apache.catalina.users.MemoryUserDatabaseFactory" name="UserDatabase" pathname="conf/tomcat-users.xml" type="org.apache.catalina.UserDatabase"/>
</GlobalNamingResources>
<!-- 一个"Service"是一个或多个"Connectors"的集合,它们共享
单个"Container"注意:"Service"本身不是"Container",
因此您不能在此级别定义子组件,例如"Valves"。
文档位于/docs/config/service.html
-->
<Service name="Catalina">
<!--连接器可以使用共享的执行程序,您可以定义一个或多个具名线程池-->
<!--
<Executor name="tomcatThreadPool" namePrefix="catalina-exec-"
maxThreads="150" minSpareThreads="4"/>
-->
<!-- 一个"Connector"代表通过该端点接收请求的端点
和返回响应。文档位于:
HTTP连接器:/docs/config/http.html
AJP连接器:/docs/config/ajp.html
定义一个非SSL/TLS HTTP/1.1连接器,端口为8080
-->
<Connector connectionTimeout="20000" port="18080" protocol="HTTP/1.1" redirectPort="18443" server="Apache TomEE" xpoweredBy="false"/>
<!-- 使用共享线程池的"Connector"-->
<!--
<Connector executor="tomcatThreadPool"
port="18080" protocol="HTTP/1.1"
<details>
<summary>英文:</summary>
From the TomEE 9 console I can see that the activeMQ KahaDB is using `C:\Windows\system32\activemq-data\localhost\KahaDB` as its default storage directory.
Jun 29, 2023 10:00:01 AM org.apache.activemq.broker.BrokerService <clinit>
INFO: Loaded the Bouncy Castle security provider at position: 14
Jun 29, 2023 10:00:02 AM org.apache.activemq.broker.BrokerService
doStartPersistenceAdapter
INFO: Using Persistence Adapter:
KahaDBPersistenceAdapter[C:\Windows\system32\activemq-data\localhost\KahaDB]
Jun 29, 2023 10:00:02 AM org.apache.activemq.store.SharedFileLocker doStart
INFO: Database activemq-data\localhost\KahaDB\lock is locked by another server. This broker is now in slave mode waiting a lock to be acquired
Since `C:\Windows\system32` is a protected folder it is causing the ActiveMQ to split out this INFO
INFO: Database activemq-data\localhost\KahaDB\lock is locked by another server. This broker is now in slave mode waiting a lock to be acquired
I found [this link][1] that documents how I can custom configure the KahaDB. The configuration looks simple enough, but
I just don't know what xml file to put the configuration in? I tried `server.xml` but it didn't work.
[1]: https://activemq.apache.org/kahadb
Below is the content of server.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!--
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.
--><!-- Note: A "Server" is not itself a "Container", so you may not
define subcomponents such as "Valves" at this level.
Documentation at /docs/config/server.html
-->
<Server port="18005" shutdown="SHUTDOWN">
<!-- TomEE plugin for Tomcat -->
<Listener className="org.apache.tomee.catalina.ServerListener"/>
<Listener className="org.apache.catalina.startup.VersionLoggerListener"/>
<!-- Security listener. Documentation at /docs/config/listeners.html
<Listener className="org.apache.catalina.security.SecurityListener" />
-->
<!-- APR library loader. Documentation at /docs/apr.html -->
<Listener SSLEngine="on" className="org.apache.catalina.core.AprLifecycleListener"/>
<!-- Prevent memory leaks due to use of particular java/javax APIs-->
<Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener"/>
<Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener"/>
<Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener"/>
<!-- Global JNDI resources
Documentation at /docs/jndi-resources-howto.html
-->
<GlobalNamingResources>
<!-- Editable user database that can also be used by
UserDatabaseRealm to authenticate users
-->
<Resource auth="Container" description="User database that can be updated and saved" factory="org.apache.catalina.users.MemoryUserDatabaseFactory" name="UserDatabase" pathname="conf/tomcat-users.xml" type="org.apache.catalina.UserDatabase"/>
</GlobalNamingResources>
<!-- A "Service" is a collection of one or more "Connectors" that share
a single "Container" Note: A "Service" is not itself a "Container",
so you may not define subcomponents such as "Valves" at this level.
Documentation at /docs/config/service.html
-->
<Service name="Catalina">
<!--The connectors can use a shared executor, you can define one or more named thread pools-->
<!--
<Executor name="tomcatThreadPool" namePrefix="catalina-exec-"
maxThreads="150" minSpareThreads="4"/>
-->
<!-- A "Connector" represents an endpoint by which requests are received
and responses are returned. Documentation at :
HTTP Connector: /docs/config/http.html
AJP Connector: /docs/config/ajp.html
Define a non-SSL/TLS HTTP/1.1 Connector on port 8080
-->
<Connector connectionTimeout="20000" port="18080" protocol="HTTP/1.1" redirectPort="18443" server="Apache TomEE" xpoweredBy="false"/>
<!-- A "Connector" using the shared thread pool-->
<!--
<Connector executor="tomcatThreadPool"
port="18080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="18443" />
-->
<!-- Define an SSL/TLS HTTP/1.1 Connector on port 8443 with HTTP/2
This connector uses the NIO implementation. The default
SSLImplementation will depend on the presence of the APR/native
library and the useOpenSSL attribute of the AprLifecycleListener.
Either JSSE or OpenSSL style configuration may be used regardless of
the SSLImplementation selected. JSSE style configuration is used below.
-->
<!--
<Connector port="18443" protocol="org.apache.coyote.http11.Http11NioProtocol"
maxThreads="150" SSLEnabled="true" xpoweredBy="false" server="Apache TomEE" >
<UpgradeProtocol className="org.apache.coyote.http2.Http2Protocol" />
<SSLHostConfig>
<Certificate certificateKeystoreFile="conf/localhost-rsa.jks"
type="RSA" />
</SSLHostConfig>
</Connector>
-->
<!-- Define an AJP 1.3 Connector on port 18009 -->
<!--
<Connector protocol="AJP/1.3"
address="::1"
port="18009"
redirectPort="18443" />
-->
<!-- An Engine represents the entry point (within Catalina) that processes
every request. The Engine implementation for Tomcat stand alone
analyzes the HTTP headers included with the request, and passes them
on to the appropriate Host (virtual host).
Documentation at /docs/config/engine.html -->
<!-- You should set jvmRoute to support load-balancing via AJP ie :
<Engine name="Catalina" defaultHost="localhost" jvmRoute="jvm1">
-->
<Engine defaultHost="localhost" name="Catalina">
<broker brokerName="broker">
<persistenceAdapter>
<kahaDB directory="/var/apps/apache-tomee-plus-9.0/boss/usmo/Boss/resources/jmsQueues" journalMaxFileLength="32mb"/>
</persistenceAdapter>
</broker>
<!--For clustering, please take a look at documentation at:
/docs/cluster-howto.html (simple how to)
/docs/config/cluster.html (reference documentation) -->
<!--
<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"/>
-->
<!-- Use the LockOutRealm to prevent attempts to guess user passwords
via a brute-force attack -->
<Realm className="org.apache.catalina.realm.LockOutRealm">
<!-- This Realm uses the UserDatabase configured in the global JNDI
resources under the key "UserDatabase". Any edits
that are performed against this UserDatabase are immediately
available for use by the Realm. -->
<Realm className="org.apache.catalina.realm.UserDatabaseRealm" resourceName="UserDatabase"/>
</Realm>
<Host appBase="webapps" autoDeploy="true" name="localhost" unpackWARs="true">
<!-- SingleSignOn valve, share authentication between web applications
Documentation at: /docs/config/valve.html -->
<!--
<Valve className="org.apache.catalina.authenticator.SingleSignOn" />
-->
<!-- Access log processes all example.
Documentation at: /docs/config/valve.html
Note: The pattern used is equivalent to using pattern="common" -->
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs" pattern="%h %l %u %t &quot;%r&quot; %s %b" prefix="localhost_access_log" suffix=".txt"/>
<Context docBase="Boss" path="/Boss" reloadable="true" source="org.eclipse.jst.jee.server:Boss"/><Context docBase="dhiveCommandXML" path="/dhiveCommandXML" reloadable="true" source="org.eclipse.jst.jee.server:dhiveCommandXML"/></Host>
</Engine>
</Service>
</Server>
</details>
# 答案1
**得分**: 1
`server.xml` 是放置 KahaDB 配置的正确位置。如果这没有起作用,请将整个 `server.xml` 和启动日志发布出来,我们来看一下。关键是你需要 _编辑_ 现有的持久化适配器部分,修改成如下所示:
```xml
<broker>
....
<persistenceAdapter>
<kahaDB directory="c:\var\activemq\data\kahadb" journalMaxFileLength="32mb" />
</persistenceAdapter>
...
</broker>
请注意,你在日志目录上也会遇到相同的问题。最好将整个 activemq 安装放在除了 system32
以外的地方。
编辑
我误解了这个 activemq 实例是 TomEE 运行时的一部分。server.xml
不是正确的位置。答案稍微复杂一些。
首先,对于任何严肃的 ActiveMQ 部署,我会单独运行代理程序。这只是一个良好的做法,但如果你只有一个 TomEE 服务器,使用嵌入式实例就可以了。要配置这个,请将以下内容放在 tomee.xml
中,并根据你的需要调整大小。
<Resource
id="ra/activemq"
type="ActiveMQResourceAdapter">
BrokerXmlConfig=
ServerUrl=ssl://your.activemq.host.com:61616
threadPoolSize=10
maximumRedeliveries=0
</Resource>
<Resource
id="jms/xaConnectionFactory"
type="javax.jms.ConnectionFactory">
ResourceAdapter=ra/activemq
poolMaxSize=10
poolMinSize=1
connectionMaxWaitTime=5s
transactionSupport=xa
</Resource>
如果你真的想运行一个本地代理并且想要配置它,你需要创建一个带有你想要的任何设置的 activemq.xml
。首先从他们的 git 主分支中复制 activemq.xml
,然后更改你想要的设置。在这种情况下,你会改变 kahadb 的位置。
接下来告诉 TomEE 使用你的配置,你需要从 xml 加载代理程序配置:
<Resource
id="ra/activemq"
type="ActiveMQResourceAdapter">
BrokerXmlConfig=xbean:activemq.xml
ServerUrl=vm://localhost
threadPoolSize=10
maximumRedeliveries=0
</Resource>
<Resource
id="jms/xaConnectionFactory"
type="javax.jms.ConnectionFactory">
ResourceAdapter=ra/activemq
poolMaxSize=10
poolMinSize=1
connectionMaxWaitTime=5s
transactionSupport=xa
</Resource>
- 你可能需要将一些 jar 文件添加到
tomee/lib
目录以支持 XML 配置。你可以从 activemq 的下载中获取这些文件。 vm://localhost
表示与本地代理的连接。确保你的代理名称是localhost
,或者在两个位置都进行更改。
希望对你有所帮助!
英文:
server.xml
is the correct place to put the KahaDB configuration. If this did not work, go ahead and post your entire server.xml
and startup log, and let's take a look. The key is you'll have to edit the existing persistence adapter section to something like this:
<broker>
....
<persistenceAdapter>
<kahaDB directory="c:\var\activemq\data\kahadb" journalMaxFileLength="32mb" />
</persistenceAdapter>
...
</broker>
Note that you'll have the same problem with the log directory. It's probably best you put the entire activemq installation somewhere other than system32
.
EDIT
I misunderstood that this activemq instace was a part of a TomEE runtime. server.xml
is not the correct place. The answer is a bit more complicated.
First, for any serious deployment of ActiveMQ, I would run the broker externally. This is just good practice, but if you have exactly one TomEE server, using the embedded instance is fine. To configure this, put the following in tomee.xml
and adjust the sizes for your needs.
<Resource
id="ra/activemq"
type="ActiveMQResourceAdapter">
BrokerXmlConfig=
ServerUrl=ssl://your.activemq.host.com:61616
threadPoolSize=10
maximumRedeliveries=0
</Resource>
<Resource
id="jms/xaConnectionFactory"
type="javax.jms.ConnectionFactory">
ResourceAdapter=ra/activemq
poolMaxSize=10
poolMinSize=1
connectionMaxWaitTime=5s
transactionSupport=xa
</Resource>
If you really want to run a local broker and want to configure it, you need to create an activemq.xml
with whatever settings you want. Start by copying the activemq.xml
from their git master, and change the settings you want. In this case, you would change the kahadb location.
Next to tell TomEE to use your configuration, you need to load the broker configuration from xml:
<Resource
id="ra/activemq"
type="ActiveMQResourceAdapter">
BrokerXmlConfig=xbean:activemq.xml
ServerUrl=vm://localhost
threadPoolSize=10
maximumRedeliveries=0
</Resource>
<Resource
id="jms/xaConnectionFactory"
type="javax.jms.ConnectionFactory">
ResourceAdapter=ra/activemq
poolMaxSize=10
poolMinSize=1
connectionMaxWaitTime=5s
transactionSupport=xa
</Resource>
- You'll probably need to add some jar files to the
tomee/lib
directory for XML config support. You can grab these from an activemq download vm://localhost
indicates the connection to the local broker. Make sure your broker name islocalhost
, or change it in both places.
Hope this helps!
答案2
得分: 1
最终能够配置TOMEE activemq以使用不同的KahaDB文件夹。我按照文档中的说明进行操作(请参阅“内部ActiveMQ Broker和activemq.xml”部分)。以下是说明的简要总结和一些附加注释:
1)下载并将以下JAR文件添加到tomee服务器的lib文件夹:
spring-beans-3.2.9.RELEASE.jar
spring-context-3.2.9.RELEASE.jar
spring-core-3.2.9.RELEASE.jar
spring-web-3.2.9.RELEASE.jar
xbean-spring-3.9.jar
activemq-spring-5.8.0.jar(文档中未提及)
activemq_spring-expression-5.3.29.jar(文档中未提及)
2)将tomee.xml添加到tomee服务器的conf文件夹。参见下面的示例。注意:BrokerXmlConfig是您指定activemq配置文件的URL的位置(注意:我无法使用相对路径(如conf/activemq.xml)。因此,我最终使用了绝对路径)
<?xml version="1.0" encoding="UTF-8"?>
<tomee>
<!-- 参见http://tomee.apache.org/containers-and-resources.html -->
<!-- 激活以下行以能够在apps中部署应用程序 -->
<!-- <Deployments dir="apps" /> -->
<Resource id="BossJmsResourceAdapter" type="ActiveMQResourceAdapter">
BrokerXmlConfig = xbean:file:/var/apps/apache-tomee-plus-9.0/boss/usmo/Boss/resources/properties/activemq.xml
ServerUrl = vm://localhost
</Resource>
<Resource id="BossJmsConnectionFactory" type="jakarta.jms.ConnectionFactory">
ResourceAdapter = BossJmsResourceAdapter
</Resource>
<Container id="BossJmsMdbContainer" ctype="MESSAGE">
ResourceAdapter = BossJmsResourceAdapter
</Container>
<Resource id="jms/TransactionManager" type="jakarta.jms.Queue"/>
<Resource id="jms/EmailManager" type="jakarta.jms.Queue"/>
</tomee>
3)在步骤2中指定的URL处创建一个activemq.xml文件。以下是一个示例:
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:amq="http://activemq.apache.org/schema/core" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd http://activemq.apache.org/schema/core http://activemq.apache.org/schema/core/activemq-core-5.8.0.xsd">
<!-- 允许我们在此配置文件中将外部属性用作变量 -->
<broker xmlns="http://activemq.apache.org/schema/core" useJmx="true" brokerName="localhost" useShutdownHook="false" persistent="true" start="false" schedulerSupport="false" enableStatistics="false" offlineDurableSubscriberTimeout="259200000" offlineDurableSubscriberTaskSchedule="3600000">
<persistenceAdapter>
<kahaDB directory="/var/apps/apache-tomee-plus-9.0/boss/usmo/Boss/resources/jmsQueues/activemq-data/kaha" indexCacheSize="20000" ignoreMissingJournalfiles="true" checkForCorruptJournalFiles="true" checksumJournalFiles="true"/>
</persistenceAdapter>
<!-- ActiveMQ将侦听的传输连接器。在tomee.xml文件的serverUrl中设置相同 -->
<transportConnectors>
<!-- 本地代理 -->
<transportConnector name="vm" uri="vm://localhost"/>
</transportConnectors>
</broker>
</beans>
英文:
Finally able to configure the TOMEE activemq to use a different KahaDB folder. I followed the instructions from document (See the 'Internal ActiveMQ Broker with activemq.xml' section). Below is a short summary of the instructions and some additional notes:
- download and add the following jars into tomee server's lib folder:
spring-beans-3.2.9.RELEASE.jar
spring-context-3.2.9.RELEASE.jar
spring-core-3.2.9.RELEASE.jar
spring-web-3.2.9.RELEASE.jar
xbean-spring-3.9.jar
activemq-spring-5.8.0.jar (This one is not mentioned in the document)
activemq_spring-expression-5.3.29.jar (This one is not mentioned in the document)
- Add tomee.xml to the tomee server's conf folder. See example below. Note: BrokerXmlConfig is where you specify the url of the activemq configure file (note: I was unable to use relative path (like conf/activemq.xml). So, I ended up using absolute path)
<?xml version="1.0" encoding="UTF-8"?>
<tomee>
<!-- see http://tomee.apache.org/containers-and-resources.html -->
<!-- activate next line to be able to deploy applications in apps -->
<!-- <Deployments dir="apps" /> -->
<Resource id="BossJmsResourceAdapter" type="ActiveMQResourceAdapter">
BrokerXmlConfig = xbean:file:/var/apps/apache-tomee-plus-9.0/boss/usmo/Boss/resources/properties/activemq.xml
ServerUrl = vm://localhost
</Resource>
<Resource id="BossJmsConnectionFactory" type="jakarta.jms.ConnectionFactory">
ResourceAdapter = BossJmsResourceAdapter
</Resource>
<Container id="BossJmsMdbContainer" ctype="MESSAGE">
ResourceAdapter = BossJmsResourceAdapter
</Container>
<Resource id="jms/TransactionManager" type="jakarta.jms.Queue"/>
<Resource id="jms/EmailManager" type="jakarta.jms.Queue"/>
</tomee>
- Create an activemq.xml file at the url specified in step 2. Below is an example of one:
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:amq="http://activemq.apache.org/schema/core" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd http://activemq.apache.org/schema/core http://activemq.apache.org/schema/core/activemq-core-5.8.0.xsd">
<!-- Allows us to use external properties as variables in this configuration file -->
<broker xmlns="http://activemq.apache.org/schema/core" useJmx="true" brokerName="localhost" useShutdownHook="false" persistent="true" start="false" schedulerSupport="false" enableStatistics="false" offlineDurableSubscriberTimeout="259200000" offlineDurableSubscriberTaskSchedule="3600000">
<persistenceAdapter>
<kahaDB directory="/var/apps/apache-tomee-plus-9.0/boss/usmo/Boss/resources/jmsQueues/activemq-data/kaha" indexCacheSize="20000" ignoreMissingJournalfiles="true" checkForCorruptJournalFiles="true" checksumJournalFiles="true"/>
</persistenceAdapter>
<!-- The transport connectors ActiveMQ will listen to. Set the same for the serverUrl in the tomee.xml file -->
<transportConnectors>
<!-- Local broker -->
<transportConnector name="vm" uri="vm://localhost"/>
</transportConnectors>
</broker>
</beans>
答案3
得分: 0
将此 activemq/activmq-data 目录移动到非 system32 位置会有什么阻碍?
参考链接:https://activemq.apache.org/version-5-getting-started.html
下载为 zip 文件,并使其在易于访问的位置运行。
英文:
What stops you to move this activemq/activmq-data directory to non-system32 location?
refer: https://activemq.apache.org/version-5-getting-started.html
Download as zip and make it working from easy location.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论