Quantcast
Channel: Oracle Blogs 日本語のまとめ
Viewing all articles
Browse latest Browse all 760

[WLS, Java, FMW] Application MBeans Visibility in Oracle WebLogic Server 12.2.1

$
0
0
原文はこちら。
https://blogs.oracle.com/WebLogicServer/entry/application_mbeans_visibility_in_oracle

Oracle WebLogic Server (WLS) 12.2.1では、 Multi-Tenancy (WLS MT)と呼ばれる機能をサポートしています。WLS MTではパーティション、パーティション管理者、パーティションリソースというコンセプトが導入されました。ドメイン中のリソース、例えばMBeanにアクセスする際にパーティション分離を強制します。WLS管理者はドメインやパーティションのMBeanを見ることができますが、パーティション管理者だけでなく他のパーティションロールは自身のパーティションにあるMBeanしか見ることはできません。

このエントリでは、アプリケーションMBeanの可視性サポートを説明して、WLS MT 12.2.1のパーティション分離を紹介します。この説明には以下の内容をふくみます
  • WLS MTでのアプリケーションMBeanの可視性に関する概要
  • どのMBeanがWLS MBeanServerに登録され、どのMBeanがWLS管理者もしくはパーティション管理者から見えるのかを説明するシンプルなユーザーケース
  • 詳細情報の参考記事へのリンク
このエントリで取り上げるユースケースは以下のエントリで作成したドメインを基にしています。
Create WebLogic Server Domain with Partitions using WLST in 12.2.1
https://blogs.oracle.com/WebLogicServer/entry/create_weblogic_server_domain_with
http://orablogs-jp.blogspot.jp/2015/11/create-weblogic-server-domain-with.html 
このエントリでは、以下の内容を説明しています。
  • ドメイントポロジの概要紹介
  • ドメインやパーティションへのアプリケーションデプロイ方法の説明
  • JMXクライアントからグローバル/ドメインURLやパーティション固有のURLで、アプリケーションMBeanにアクセスする方法の説明
  • デバッグ、ロギングの有効化方法の説明

1. Overview

アプリケーションをパーティション毎にWebLogic Serverにデプロイできるので、アプリケーションを複数のパーティションに対して複数配置します。WebLogic Serverには以下のMBeanServerがあります。
  • Domain Runtime MBeanServer
  • Runtime MBeanServer
各MBeanServerをすべてのパーティション用に使うことができます。WebLogic Serverはアプリケーションが各MBeanServerに登録したMBeanが各パーティションで一意であることを保証する必要があります。

WLS MTでのアプリケーションMBeanの可視性をいくつかのパートで説明します。
  • Partition Isolation
  • Application MBeans Registration
  • Query Application MBeans
  • Access Application MBeans

1.1 Partition Isolation

WLS管理者はパーティションMBeanを見ることができますが、パーティション管理者はドメインや他のパーティションのMBeanを見ることはできません。

1.2 Application MBeans Registration

アプリケーションをパーティションにデプロイする間にアプリケーションMBeanを登録します。WebLogic ServerはアプリケーションMBeanをWLS MBeanServerに登録する際に、パーティション固有のキー(例:Partition=<パーティション名>)をMBean Object Namesに追加します。こうして、増加したアプリケーションから登録された場合に、MBeanオブジェクト名が一意になることを保証します。

右図では、ドメインやパーティションのWLS MBeanServerに登録した際にアプリケーションMBean Object Nameが異なるさまを示しています。


右図ではWebLogicドメインとアプリケーションがあることを示しています。

WebLogicドメインは2個のパーティション(cokeとpepsi)で構成されています。

アプリケーションデプロイ中にアプリケーションがMBeanを登録します(例:testDomain:type=testType)。

アプリケーションをWebLogicドメイン、cokeパーティション、pepsiパーティションにデプロイします。WLS MBeanServerインスタンスはドメイン、cokeパーティション、pepsiパーティションが共有しています。
3回のアプリケーションデプロイメントの結果、3個のアプリケーションMBeanが同じMBeanServerに登録されています。
  • ドメイン所属のMBean:          testDomain:type=testType
  • cokeパーティション所属のMBean: testDomain:Partition=cokePartition,type=testType
  • pepsiパーティション所属のMBean: testDomain:Partition=pepsiPartition,type=testType
パーティションに属するMBeanには、ObjectNameのPartitionキープロパティが含まれています。

1.3 Query Application MBeans

WebLogic WLSTやJConsoleといったJMXクライアントでグローバル/ドメインURLやパーティション固有のURLに接続し、WebLogic MBeanServerに対しクエリを実行すると、異なるクエリ結果が返ってきます。
  • グローバル/ドメインURLに接続する場合、パーティション所属のアプリケーションMBeanは接続したJMXクライアントから見える。
  • パーティション固有のURLに接続する場合、WebLogic Serverがクエリ結果にフィルタを掛け、パーティション所属のアプリケーションMBeanのみ返す。ドメインや他パーティション所属のMBeanは見えない。

1.4 Access Application MBeans

WebLogic WLSTやJConsoleといったJMXクライアントがパーティション固有のURLに接続し、 getAttribute(<MBean ObjectName>, <attributeName>)のようなJMXの操作を実行すると、実のところ異なるMBeanに対してJMX操作を実行します。
  • グローバル/ドメインURLに接続する場合、ドメイン所属のMBean(MBean ObjectNameでPartitionキープロパティのないMBean)のgetAttribute()を呼び出す
  • パーティション固有のURLに接続する場合、パーティション所属のMBean(MBean ObjectNameでPartitionキープロパティがあるMBean)のgetAttribute()を呼び出す

2. Use case

それでは、MBeanの可視性がWebLogic Server 12.2.1のMultitenancyでパーティション分離をサポートするためにどのように作用するのか説明します。

2.1 Domain with Partitions

以下のエントリで、2個のパーティション(cokeとpepsi)を持つドメインを作成しています。
Create WebLogic Server Domain with Partitions using WLST in 12.2.1
https://blogs.oracle.com/WebLogicServer/entry/create_weblogic_server_domain_with
http://orablogs-jp.blogspot.jp/2015/11/create-weblogic-server-domain-with.html 
このドメインを再度このエントリのユースケースのために利用します。以下はドメイントポロジのサマリです。
  • ドメインは1個の管理サーバ(admin)、cokeとpepsiというパーティションで構成されている。
  • cokeパーティションには1個のリソースグループ (coke-rg1) を含み、仮想ターゲット(coke-vt)に向けられている
  • pepsiパーティションには1個のリソースグループ (pepsi-rg1) を含み、仮想ターゲット(pepsi-vt)に向けられている
より具体的に、各ドメイン/パーティションには以下の値で構成されています。
Domain NameUser NamePassword
Domainbase_domainweblogicwelcome1
Coke Partitioncokemtadmin1welcome1
Pepsi Partitionpepsimtadmin2welcome2
このドメイン作成方法の詳細は上記リンクをご覧ください。

2.2 Application deployment

ドメインをセットアップして開始し、アプリケーション"helloTenant.ear"をドメインにデプロイします。パーティションcokeのリソースグループcoke-rg1とパーティションpepsiのリソースグループpepsi-rg1にもデプロイします。デプロイはWLST、Fusion Middleware ControlといったWebLogic Serverのツールで可能です。以下はドメインとパーティションにアプリケーションをデプロイするためのWLSTコマンドの例です。
startEdit()
deploy(appName='helloTenant',target='admin,path='${path-to-the-ear-file}/helloTenant.ear')
deploy(appName='helloTenant-coke',partition='coke',resourceGroup='coke-rg1',path='${path-to-the-ear-file}/helloTenant.ear')
deploy(appName='helloTenant-pepsi',partition='pepsi',resourceGroup='pepsi-rg1',path='${path-to-the-ear-file}/helloTenant.ear')
save()
activate()
別のWLSデプロイメントツールについては、Referenceセクションをご覧ください。

2.3 Access Application MBeans

アプリケーションデプロイメントの間に、アプリケーションMBeanがWebLogic Server MBeanServerに登録されます。[1.2 Application MBean Registration]でお伝えした通り、1個のアプリケーションしかないにもかかわらず、複数のMBeanが登録されています。

アプリケーションMBeansにアクセスするには複数の方法があります。
  • WLST
  • JConsole
  • JSR 160 apis

2.3.1 WLST

WebLogic Scripting Tool (WLST) はコマンドラインスクリプティングインターフェースで、システム管理者やオペレータがWebLogic Serverインスタンスやドメインを監視・管理するために使います。WLSTを開始するには以下のコマンドを実行します。
$MW_HOME/oracle_common/common/bin/wlst.sh
WLSTが起動すると、ユーザーは接続URLを指定してサーバに接続することができます。以下は異なるアプリケーションMBean属性の値を示しています。WebLogic Server管理者やパーティション管理者が異なる接続URLを使った際には異なるアプリケーションMBean属性値が表示されます。

2.3.1.1 WLS administrator

WebLogic Server管理者 'weblogic'は以下の接続コマンドを使ってドメインに接続します。
connect("weblogic", "welcome1", "t3://localhost:7001")
下図はWebLogic Server MBeanServerに登録された3個のMBeanを示しています。なお、ドメイン名はtest.domain、各MBeanのPartitionName属性値は以下の通りです。
  • test.domain:Partition=coke,type=testType,name=testName
    • パーティションcokeに属している。PartitionName属性値はcoke
  • test.domain:Partition=pepsi,type=testType,name=testName
    • パーティションpepsiに属している。PartitionName属性値はpepsi
  • test.domain:type=testType,name=testName
    • ドメインに属している。ObjectNameのPartitionキープロパティはない。PartitionName属性はDOMAIN
パーティション所属のMBeanにはObjectName中にPartitionキープロパティがあります。パーティションコンテキストに登録する際にWebLogic Serverが内部でPartitionキープロパティを追加します。


2.3.1.2 Partition administrator for coke

同様に、パーティションcokeの管理者mtadmin1はパーティションcokeに接続できます。接続URLは仮想ターゲットcoke-vt(<Domain_Home>/config/config.xmlをチェックしてください)で定義されたURI接頭辞である/cokeを使います。
connect("mtadmin1", "welcome1", "t3://localhost:7001/coke")
下図のように、パーティションcokeに接続すると、1個だけMBeanが表示されます。
test.domain:type=testType,name=testName
PartitionキープロパティがObjectNameにありませんが、このMBeanはパーティションcokeに属しています。PartitionName属性値はcokeです。

2.3.1.3 Partition administrator for pepsi

同様に、パーティションpepsiの管理者mtadmin2はパーティションpepsiに接続できます。接続URLは仮想ターゲットpepsi-vt(<Domain_Home>/config/config.xmlをチェックしてください)で定義されたURI接頭辞である/pepsi を使います。
connect("mtadmin2", "welcome2", "t3://localhost:7001/pepsi")
下図のように、パーティションpepsiに接続すると、1個だけMBeanが表示されます。
test.domain:type=testType,name=testName
PartitionキープロパティがObjectNameにありませんが、パーティションcokeの管理者の場合と同様、このMBeanはパーティションpepsiに属しています。PartitionName属性値はpepsiです。

2.3.2 JConsole

JConsoleはJDKに組み込まれたGUIのツールで、Java Management Extensions (JMX)仕様に準拠した監視ツールです。JConsoleを使うと、MBeanServerに登録されたMBeanの概要をつかむことができます。

JConsoleを起動するには以下のコマンドを実行します。
$JAVA_HOME/bin/jconsole
-J-Djava.class.path=$JAVA_HOME/lib/jconsole.jar:
$JAVA_HOME/lib/tools.jar:$MW_HOME/wlserver/server/lib/wljmxclient.jar
 -J-Djmx.remote.protocol.provider.pkgs=weblogic.management.remote
ここで<MW_HOME>はWebLogic Serverをインストールした場所です。

JConsoleが起動したら、WebLogic Server管理者やパーティション管理者は資格証明とJMXサービスURLを指定した後に、MBeanを確認することができます。

2.3.2.1 WLS administrator

WebLogic Server管理者weblogicはJMXサービスURLを指定して、WebLogic Server Runtime MBeamServerに以下のように接続します。
service:jmx:t3://localhost:7001/jndi/weblogic.management.mbeanservers.runtime
WebLogic Server管理者が接続すると、JConsoleのMBeanツリーにはObjectNameにtest.domainを持つ3個のMBeanが表示されています。

下図の右ペインで示したObjectNameはパーティションcokeに属しており、Partitionキープロパティを有しています(Partition=coke)。


下図の場合パーティションpepsiに属するMBeanなので、Partitionキープロパティを有しています(Partition=pepsi)。



下図の場合、ドメインに属するMBeanなので、Partitionキープロパティはありません。



WebLogic Server管理者がWLSTで見たものとここで表示した結果は同じですね。

2.3.2.2 Partition administrator for coke

パーティション管理者mtadmin1は異なるJMXサービスURLをJConsoleに指定します。
service:jmx:t3://localhost:7001/coke/jndi/weblogic.management.mbeanservers.runtime
パーティション固有のJMXサービスURLを使って接続すると、パーティション管理者には1個のMBeanしか見えません。
test.domain:type=testType,name=testName
このMBeanはパーティションcokeに属しており、下図の通り、PartitionName属性値はcokeです。しかし、ObjectNameにPartitionキープロパティはありません。




2.3.2.3 Partition administrator for pepsi

パーティション管理者mtadmin2は異なるJMXサービスURLをJConsoleに指定します。
service:jmx:t3://localhost:7001/pepsi/pepsi/weblogic.management.mbeanservers.runtime
パーティション固有のJMXサービスURLを使って接続すると、パーティション管理者mtadmin2には1個のMBeanしか見えません。
test.domain:type=testType,name=testName
このMBeanはパーティションpepsiに属しており、下図の通り、PartitionName属性値はpepsiです。




2.3.3 JSR 160 APIs

JMXクライアントはJSR 160 APIを使ってMBeanServerに登録されたMBeanにアクセスすることができます。例えば以下のコードでは、サービスURLと環境をMBean属性に指定してJMXConnetorを取得しています。
import javax.management.*;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXServiceURL;
import javax.management.remote.JMXConnectorFactory;
import java.util.*

public class TestJMXConnection {
public static void main(String[] args) throws Exception {
JMXConnector jmxCon = null;
try {
// Connect to JMXConnector
JMXServiceURL serviceUrl = new JMXServiceURL(
"service:jmx:t3://localhost:7001/jndi/weblogic.management.mbeanservers.runtime");
Hashtable env = new Hashtable();
env.put(JMXConnectorFactory.PROTOCOL_PROVIDER_PACKAGES, "weblogic.management.remote");
env.put(javax.naming.Context.SECURITY_PRINCIPAL, "weblogic");
env.put(javax.naming.Context.SECURITY_CREDENTIALS, "welcome1");
jmxCon = JMXConnectorFactory.newJMXConnector(serviceUrl, env);
jmxCon.connect();

// Access the MBean
MBeanServerConnection con = jmxCon.getMBeanServerConnection();
ObjectName oname = new ObjectName("test.domain:type=testType,name=testName,*");
Set<objectname> queryResults = (Set<objectname>)con.queryNames(oname, null);
for (ObjectName theName : queryResults) {
System.out.print("queryNames(): " + theName);
String partitionName = (String)con.getAttribute(theName, "PartitionName");
System.out.println(", Attribute PartitionName: " + partitionName);
}
} finally {
if (jmxCon != null)
jmxCon.close();
このコードをコンパイルして実行するためには、wljmxclient.jarをクラスパスに指定する必要があります。
$JAVA_HOME/bin/java -classpath $MW_HOME/wlserver/server/lib/wljmxclient.jar:. TestJMXConnection
以下のような結果が出力されるはずです。
Connecting to: service:jmx:t3://localhost:7001/jndi/weblogic.management.mbeanservers.runtime
queryNames(): test.domain:Partition=pepsi,type=testType,name=testName, Attribute PartitionName: pepsi
queryNames(): test.domain:Partition=coke,type=testType,name=testName,  Attribute PartitionName: coke
queryNames(): test.domain:type=testType,name=testName, Attribute PartitionName: DOMAIN
パーティション管理者mtadmin1を使うようにコードを変更すると、以下のようになります。
JMXServiceURL serviceUrl = new JMXServiceURL(
"service:jmx:t3://localhost:7001/coke/jndi/weblogic.management.mbeanservers.runtime");
env.put(javax.naming.Context.SECURITY_PRINCIPAL, "mtadmin1");
env.put(javax.naming.Context.SECURITY_CREDENTIALS, "welcome1");
コードを実行すると、1個のMBeanしか返ってこないことがわかります。
Connecting to: service:jmx:t3://localhost:7001/coke/jndi/weblogic.management.mbeanservers.runtime
queryNames(): test.domain:type=testType,name=testName,  Attribute PartitionName: coke
同様の結果がパーティション管理者pepsiを使った場合に確認できるでしょう。パーティションpepsi固有のJMXサービスURLを指定すると、パーティションpepsiに属するMBean1個のみが返ってきます。

2.4 Enable logging/debugging flags

WebLogic Server 12.2.1でMBeanが正しい挙動を示さないことがあります。例えば、
  • MBeanのクエリを発行した際に、パーティション管理者がグローバルドメインや別のパーティションのMBeanを見ることができる。
  • JMX例外が発生する。例えば、MBeanにアクセスする際に、javax.management.InstanceNotFoundExceptionが発生する。
エラーの切り分けのために以下のことを試行してください。
  • JConsoleの接続問題の場合、JConsole起動時にコマンドラインに -debug を追加する
  • MBeanのクエリを発行すると、パーティション管理者がグローバルドメインや別パーティションのMBeanを見ることができる場合、
    • WLSTやJConsole、JSR 160 APIといったJMXクライアントから接続している場合、サービスのホスト名が<Domain_Home>/config/config.xmlの仮想ターゲットで定義したホスト名に一致することを確認する。
    • サービスURLのURI接頭辞が<Domain_Home>/config/config.xmlの仮想ターゲットで定義したURI接頭辞と一致することを確認する。
  • JMX例外が発生した場合、例えば、MBeanにアクセスする際に、javax.management.InstanceNotFoundExceptionが発生した場合
    • MBeanがパーティションに属する場合、パーティションを開始します。アプリケーションデプロイメントはパーティションが開始しないと実行されません。
    • 以下のオプションを追加して、サーバー始動時にデバッグフラグを有効にします。
      -Dweblogic.StdoutDebugEnabled=true -Dweblogic.log.LogSeverity=Debug -Dweblogic.log.LoggerSeverity=Debug -Dweblogic.debug.DebugPartitionJMX=true -Dweblogic.debug.DebugCIC=false 
    • 対象としている特定のMBean ObjectNameをサーバーログで探します。デバッグしているMBeanが正しくパーティション・コンテキストに登録されていることを確認します。MBeanオペレーションが正しくパーティションコンテキストで呼ばれていることを確認します。
    以下はMBean"test.domain:type=testType,name=testName"のMBean登録、queryNames()、getAttribute()の呼び出しに関連するデバッグメッセージの例です。
    <Oct 21, 2015 11:36:43 PM PDT> <Debug> <PartitionJMX> <BEA-000000> <Calling register MBean test.domain:type=testType,name=testName in partition DOMAIN>
    <Oct 21, 2015 11:36:44 PM PDT> <Debug> <PartitionJMX> <BEA-000000> <Calling register MBean test.domain:Partition=coke,type=testType,name=testName in partition coke>
    <Oct 21, 2015 11:36:45 PM PDT> <Debug> <PartitionJMX> <BEA-000000> <Calling register MBean test.domain:Partition=pepsi,type=testType,name=testName in partition pepsi>
    <Oct 21, 2015 11:36:56 PM PDT> <Debug> <PartitionJMX> <BEA-000000> <queryNames on MBean test.domain:Partition=coke,type=testType,name=testName,* in partition coke>
    <Oct 21, 2015 11:36:56 PM PDT> <Debug> <MBeanCIC> <BEA-000000> <getAttribute: MBean: test.domain:Partition=coke,type=testType,name=testName, CIC: (pId = 2d044835-3ca9-4928-915f-6bd1d158f490, pName = coke, appId = helloTenant$coke, appName = helloTenant, appVersion = null, mId = null, compName = null)>
    • パーティションコンテキストが正しくない理由を確認するため、上記のデバッグフラグに加え、以下のデバッグフラグを追加して、WebLogic Serverを始動してください。
      -Dweblogic.debug.DebugCIC=true. Once this flag is used, there are a lot of messages logged into the server log. Search for the messages logged by DebugCIC logger, like  ExecuteThread: '<thread id #>' for queue: 'weblogic.kernel.Default (self-tuning)'): Pushed 
      以下はDebugPartitionJMX ロガーがログ出力したメッセージの例です。
    <Oct 21, 2015, 23:59:34 PDT> INVCTXT (24-[ACTIVE] ExecuteThread: '0' for queue: 'weblogic.kernel.Default (self-tuning)'): Pushed [(pId = 0, pName = DOMAIN, appId = null, appName = null, appVersion = null, mId = null, compName = null)] on top of [(pId = 0, pName = DOMAIN, appId = null, appName = null, appVersion = null, mId = null, compName = null)]. New size is [2]. Pushed by [weblogic.application.ComponentInvocationContextManagerImpl.pushComponentInvocationContext(ComponentInvocationContextManagerImpl.java:173)
    ...
    <Oct 21, 2015 11:59:34 PM PDT> <Debug> <PartitionJMX> <BEA-000000> <Calling register MBean test.domain:type=testType,name=testName in partition DOMAIN>
    ...
    <Oct 21, 2015, 23:59:37 PDT> INVCTXT (29-[STANDBY] ExecuteThread: '2' for queue: 'weblogic.kernel.Default (self-tuning)'): Pushed [(pId = 2d044835-3ca9-4928-915f-6bd1d158f490, pName = coke, appId = helloTenant$coke, appName = helloTenant, appVersion = null, mId = null, compName = null)] on top of [(pId = 2d044835-3ca9-4928-915f-6bd1d158f490, pName = coke, appId = null, appName = null, appVersion = null, mId = null, compName = null)]. New size is [3]. Pushed by
    [weblogic.application.ComponentInvocationContextManagerImpl.pushComponentInvocationContext(ComponentInvocationContextManagerImpl.java:173)
    ...
    <Oct 21, 2015 11:59:37 PM PDT> <Debug> <PartitionJMX> <BEA-000000> <Calling register MBean test.domain:Partition=coke,type=testType,name=testName in partition coke>

3. Conclusion

WebLogic Server 12.2.1はMulti-Tenancy (MT)という新機能を提供しています。この機能を使って、パーティション分離が強制されています。アプリケーションをドメインやパーティションにデプロイできます。パーティションのユーザーは別のパーティションに所属するリソース(アプリケーションが登録したMBeanを含む)を見ることができません。このエントリでは、あるユースケースを使い、アプリケーションMBeanがパーティション分離が、MBeanの可視性でどのように影響するのかを御所階しました。詳細情報は、Referencesセクションをご覧ください。

4. References

Oracle® Fusion Middleware Installing and Configuring Oracle WebLogic Server and Coherence 12c (12.2.1) 
Creating and Configuring the WebLogic Domain
https://docs.oracle.com/middleware/1221/core/WLSIG/GUID-4AECC00D-782D-4E77-85DF-F74DD61391B4.htm#WLSIG281
Oracle® Fusion Middleware Installing and Configuring the Oracle Fusion Middleware Infrastructure 12c (12.2.1) 
Configuring the Oracle Fusion Middleware Infrastructure Domain
https://docs.oracle.com/middleware/1221/core/INFIN/GUID-CA80A6E9-8903-4E19-81D7-A3647A11D0A6.htm#INFIN280
Oracle® Fusion Middleware WLST Command Reference for WebLogic Server 12c (12.2.1)
https://docs.oracle.com/middleware/1221/wls/WLSTC/toc.htm
Java SE Monitoring and Management Guide
Using JConsole
http://docs.oracle.com/javase/8/docs/technotes/guides/management/jconsole.html
Managing WebLogic Server with JConsole
https://blogs.oracle.com/WebLogicServer/entry/managing_weblogic_servers_with
JSR 160: Java Management Extensions Remote JMX api
https://jcp.org/en/jsr/detail?id=160
Oracle® Fusion Middleware Administering Security for Oracle WebLogic Server 12.2.1 12c (12.2.1)
Configuring Security for a WebLogic Domain
http://docs.oracle.com/middleware/1221/wls/SECMG/conf-security-for-domain.htm#SECMG777
Oracle® Fusion Middleware Deploying Applications to Oracle WebLogic Server 12c (12.2.1)
Understanding WebLogic Server Deployment
https://docs.oracle.com/middleware/1221/wls/DEPGD/understanding.htm#DEPGD114

Viewing all articles
Browse latest Browse all 760

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>