Připojovat se z aplikace do databáze přes její síťové rozhraní je pro většinu lidí taková samozřejmost, že je ani nenapadne, že by to mohlo být jinak. Přitom s sebou tento způsob nese několik nevýhod, které ve velkém procentu deployment scénářů trpíme zcela zbytečně. Předně je to overhead průchodu síťovým stackem, pokud aplikace běží na stejném stroji jako DB. V takovém scénáři je možné použít místo připojení přes localhost nějakou jinou metodu IPC, v unixovém světě klasicky socket.
Samotný výkon se nemusí zdát jako tak závažný důvod, ale jednak je ta síťová vrstva opravdu zcela zbytečná a jednak s sebou nese další nepřímé problémy. Například může při chybě konfigurace vystavit DB na otevřenou síť, kde je snadno napadnutelná. A protože síťové rozhraní nerozlišuje mezi lokálním a vzdáleným přístupem, je nutné i pro lokální přístup autentizace aplikace (nejčastěji jménem a heslem), přestože lokálně takovou autentizaci dokáže dělat lépe operační systém a to navíc bez nějakého pitomého hesla.
Kdo někdy na linuxu používal psql
ví, jaká je to pohoda. Prostě v shellu napíšete
psql
a jste přihlášený jako lokální uživatel k jeho výchozí databázi. Chcete-li něco udělat jako privilegovaný databázový uživatel, použijete standardní prostředek OS
sudo -u postgres psql
a jste přihlášeni k databázi jako její správce. Žádná hesla, žádná speciální databázová udělátka.
Ale pak je tu bohužel Java. Unixové sockety nativně neumí a proto to pak
JDBC drivery mají každý pes jiná ves, pokud vůbec na ty sockety myslí. Tak jsem
dlouho žil s tím, že právě JDBC driver PostgreSQL sockety vůbec neumí,
ale nedávno jsem k velké radosti zjistil, že od verze 9.4 už
podporu přidali pomocí url parametrů
socketFactory
a socketFactoryArg
.
Podpora není psaná pro nějakou konkrétní knihovnu přidávající do Java Unix sockety,
ale používá jako hlavní rozhraní standardní javovský javax.net.SocketFactory
jehož implementací je možné adaptovat libovolnou socketovou knihovnu.
Ono ale bohužel ani ty socketové knihovny nejsou žádná sláva. Jedna z těch, jejíž adaptace by byla nejjednodušší, junixsocket, vypadá mrtvá a navíc se mi nechtělo dělat si separátní projekt pro ten adaptér. Naštěstí je junixsocket forknutý a mimo pár vylepšení již obsahuje právě i adaptaci přímo pro PostgreSQL JDBC driver. (Pozor: neberte to jako doporučení tohoto forku, nemám žádný důvod mu nějak zásadně věřit. Při pochybnostech jako vždy kompilujte sami.)
Tomcat JDBC pool
Protože jsem si chtěl udělat pool konexí přímo v Tomcatu (naštěstí už má Tomcat vlastní implementaci poolu) a sdílet ho přes JNDI mezi více aplikacemi, je použití ještě trochu složitější, než jen přidání závislostí do Mavenu:
- Stáhnout jary
junix-socket-common
ajunixsocket-common
anative-lib-loader
do Tomcatího libu - Přidat do
conf/server.xml
globální konfiguraci:<GlobalNamingResources> <Resource type="javax.sql.DataSource" name="jdbc/ServerDB" description="Default database for general use." factory="org.apache.tomcat.jdbc.pool.DataSourceFactory" driverClassName="org.postgresql.Driver" url="jdbc:postgresql://localhost/database?socketFactory=org.newsclub.net.unix.socketfactory.PostgresqlAFUNIXSocketFactory&socketFactoryArg=/var/run/postgresql/.s.PGSQL.5432" /> </GlobalNamingResources>
localhost
v JDBC url nemá žádný význam a může být cokoli, jen to není možné kvůli syntaxi úplně vypustitsocketFactoryArg
odkazuje na onen kýžený socket. Uvedl jsem jeho typické umístění, ale je možné si to ověřit pomocígrep unix_socket_directories /etc/postgresql/9.6/main/postgresql.conf
- Přidat do
conf/context.xml
link<ResourceLink type="javax.sql.DataSource" name="jdbc/ServerAppDB" global="jdbc/ServerDB"/>
- Přidat do
web.xml
aplikace resource ref<resource-ref> <description>DB Connection</description> <res-ref-name>jdbc/ServerAppDB</res-ref-name> <res-type>javax.sql.DataSource</res-type> <res-auth>Container</res-auth> </resource-ref>
- Ve Spring aplikaci použít data source pomocí nastavení v
application.properties
spring.datasource.jndi-name=jdbc/ServerAppDB