Maison  >  Article  >  Java  >  Créer un service intermédiaire pour un accès indépendant à la base de données

Créer un service intermédiaire pour un accès indépendant à la base de données

巴扎黑
巴扎黑original
2017-06-23 10:57:051721parcourir

Avec les changements continus dans les activités de l'entreprise, le projet A et la base de données DB_A sous-jacente ont été transformés il y a quelques années en services commerciaux de base et en bases de données de base.

Il existe de plus en plus de services Web qui souhaitent obtenir des données de la base de données DB_A, et la relation entre les projets a progressivement évolué vers ce qui suit :

Il est facile de voir qu'il y aura de nombreux problèmes selon la tendance de développement dans l'image ci-dessus (la relation de projet est une version simplifiée abstraite par les individus, la situation réelle est bien plus compliquée que cela).

a. Lorsqu'une exception se produit lors de l'exécution de webappA et n'est pas accessible, webappB/ webappC.... Les données DB_A peuvent-elles être obtenues normalement ?

b. Divers services fournis à webappB/webappC... pour obtenir les données DB_A sont concentrés dans webappA. Le volume de webappA s'étendra à l'infini horizontalement, ce que personne n'aime, n'est-ce pas. ?

c. Lors du fonctionnement du projet webappA, en plus de fournir ses propres services aux utilisateurs, il doit également prendre en compte les demandes d'autres projets pour obtenir des données, ce qui entraînera inévitablement un goulot d’étranglement en termes de performances.

Certains de ces problèmes sont déjà apparus lors de l'avancement en ligne du projet. Il est vraiment inconfortable d'avoir des arrêts de maintenance de temps en temps et se transforment en une forte gifle pour l'équipe du projet. .

Hors sujet : Selon la vitesse actuelle de développement d'Internet et l'expansion commerciale de diverses entreprises, les architectes qui peuvent prédire avec précision l'orientation de développement du projet dans un délai de deux ans/et préparer le l'expansion à l'avance est déjà très bonne.

Quelqu'un dans l'équipe du projet a proposé de contourner le projet webappA, et le reste webappB/webappC... se connecte directement à DB_A pour interagir, mais cela a été rapidement rejeté (il faut utiliser le couche d'accès à la base de données de chaque projet à redéfinir et à écrire).

La couche d'accès à la base de données peut-elle être séparée et utilisée comme un service pour permettre l'accès aux éléments autorisés ? Comme suit :

L'idée de base est de donner accès à une base de données spécifique pour un nombre illimité de N des wabapps. Cela évite non seulement le couplage entre projets, mais améliore également le taux de réutilisation de la couche d’accès aux données.

Vous avez déjà l'idée, commençons, BB n'arrive pas à résoudre le problème. Il a fallu environ deux jours pour construire et combler d'innombrables trous, et finalement cela s'est avéré aussi approprié que ce à quoi je m'attendais.

Le projet original ne peut pas être open source en raison d'une utilisation commerciale. Après avoir réorganisé la démo, il a été open source pour :.

Pour les étudiants qui ont besoin de s'entraîner dans ce domaine, tout deviendra clair une fois que vous le clonerez localement et l'exécuterez localement.

1. Couche d'interface de service

Nécessite un dap de dépendance de projet de données DB_A -service-api peut accéder au service dao-service-impl.

dao-service-api est l'interface fournie à la couche externe. La méthode de présentation finale est jar, et le projet maven peut en dépendre directement.

S'il existe d'anciens projets non-maven, utilisez maven-jar-plugin/maven-assembly-plugin pour dépendre on Les pots sont assemblés et ajoutés à la bibliothèque du projet.

2. Couche d'implémentation du service

dao-service-impl est construit par cxf + spring + druid + jpa (hibernate impl) open source bibliothèque de classes Il s'agit d'un pur service de composants back-end.

En tant que couche d'implémentation de l'interface de service, la méthode de présentation finale est la guerre, qui peut être déployée en cluster ou distribué, donnant D'autres projets fournissent des services.

La structure des répertoires est claire en un coup d'œil et la vitesse de développement est très rapide. Il a implémenté une génération de code simple (GenCodeServlet), et les interfaces et implémentations de la couche dao + de la couche webService peuvent être utilisées. être généré automatiquement.

La couche d'implémentation webService injecte l'interface de la couche dao, encapsulant 5 méthodes d'ajout, de suppression, de modification et de vérification d'une seule table. De manière générale, il n'est pas nécessaire d'écrire des méthodes redondantes et d'éviter d'écrire 90 % de SQL.

@WebService
@SOAPBinding(style = SOAPBinding.Style.RPC)public interface UserWs {/** * 通过 ID 过去单个 User 实体对象
     * cxf 传输返回对象不可为null,Dao 层获取为null时
     * 实例化返回空对象,判空时使用对象主键进行判断即可
     *
     * @param id 主键ID     */UserPO getUser(String id);/** * 通过类似的 PO 获取多个 User 实体对象
     *
     * @param userPO 对照的实体对象     */List<UserPO> listUser(UserPO userPO);/** * 通过类似的 PO 获取多个 User 实体对象
     *
     * @param userPO  对照的实体对象
     * @param orderby 排序字段
     * @param asc     是否升序     */List<UserPO> listUserOrdrBy(UserPO userPO, String orderby, Boolean asc);/** * 新增 User 实体对象
     *
     * @param userPO 要新增的对象     */UserPO addUser(UserPO userPO);/** * 更新 User 实体对象
     *
     * @param userPO 要更新的对象     */UserPO updateUser(UserPO userPO);
}

La méthode de développement est simple et grossière. Utilisez des outils pour générer de manière inversée la base de données hibernate et accédez à GenCodeServlet pour générer l'interface et l'implémentation de la couche dao/ws. .

Ajoutez des options de fichier de configuration et publiez le service cxf webService. Cela ne prend pas plus de 5 minutes.

3. Appelant du service

Le service à table unique publié est compris comme la couche d'accès à la base de données dans l'appelant, où il est spécifié dans votre projet Injectez et couplez la logique métier.

Le but de ce module est de démontrer comment intégrer les services édités par cxf.

a. Spring a été intégré au projet appelant (en fonction de dao-service-api)

    <jaxws:client id="UserWs" serviceClass="com.rambo.dsd.sys.ws.inter.UserWs"  address="${cxf.server.url}/UserWs"><jaxws:outInterceptors><ref bean="wss4JOutInterceptor"/></jaxws:outInterceptors></jaxws:client>

Utilisation spécifique (dans le cadre de l'injection de ressort)

        Map<String, Object> map = new HashMap<>();
        UserWs userWs = (UserWs) SpringContextUtil.getBean("UserWs");
        UserPO user = userWs.getUser("031e7a36972e11e6acede16e8241c0fe");
        map.put("1.获取单个用户:", user);

        user.setPhone("18975468245");
        UserPO userPO1 = userWs.updateUser(user);
        map.put("2.更新单个用户:", userPO1);

        UserPO userPO2 = new UserPO();
        userPO2.setName("rambo");
        userPO2.setPasswd(SecurityUtil.encryptMD5("123456"));
        userPO2.setSex("男");
        userPO2.setYxbz("Y");
        UserPO userPO3 = userWs.addUser(userPO2);
        map.put("3.新增单个用户:", userPO3);

        UserPO userPO4 = new UserPO();
        userPO4.setSex("男");
        List<UserPO> userPOList = userWs.listUser(userPO4);
        map.put("4.获取所有的男用户:", userPOList);

        UserPO userPO5 = new UserPO();
        userPO5.setSex("男");
        List<UserPO> userPOList1 = userWs.listUserOrdrBy(userPO5, "sorts", true);
        map.put("5.获取所有的男用户并按照 sorts 字段排序:", userPOList1);return map;

b. Spring n'est pas intégré dans le projet appelant (dépend de dao-service-api)

Utilisez des outils ou des commandes pour générer des clients de service cxf et introduire l'usine mode Obtenez l'instance de service où elle est utilisée et couplez-la.

            UserWsImplService userWsImplService = new UserWsImplService(new URL(cxfServerUrl + "/UserWs?wsdl"));
            UserWs userWs = userWsImplService.getUserWsImplPort();
            addWSS4JOutInterceptor(userWs);

            UserPO user = userWs.getUser("031e7a36972e11e6acede16e8241c0fe");
            map.put("1.获取单个用户:", user);

            user.setPhone("18975468245");
            UserPO userPO1 = userWs.updateUser(user);
            map.put("2.更新单个用户:", userPO1);

            UserPO userPO2 = new UserPO();
            userPO2.setUuid(StringUtil.getUUID());
            userPO2.setName("rambo");
            userPO2.setPasswd(SecurityUtil.encryptMD5("123456"));
            userPO2.setSex("男");
            userPO2.setYxbz("Y");
            UserPO userPO3 = userWs.addUser(userPO2);
            map.put("3.新增单个用户:", userPO3);

            UserPO userPO4 = new UserPO();
            userPO4.setSex("男");
            UserPOArray userPOArray1 = userWs.listUser(userPO4);
            map.put("4.获取所有的男用户:", userPOArray1);

            UserPO userPO5 = new UserPO();
            userPO5.setSex("男");
            UserPOArray userPOArray2 = userWs.listUserOrdrBy(userPO5, "sorts", true);
            map.put("5.获取所有的男用户并按照 sorts 字段排序:", userPOArray2.getItem());

4. Mécanisme d'authentification de sécurité cxf

cxf utilise le protocole de communication Soap, après tout, c'est le cas. diffusés au monde extérieur Pour les services, la sécurité reste très importante.

L'authentification de sécurité est introduite dans l'implémentation de l'intercepteur cxf ws-security wss4j, ajoutant des informations d'authentification à l'en-tête du savon.

a. Configuration du serveur

   <!--服务端安全认证回调函数--><bean id="serverAuthCallback" class="com.rambo.dsd.base.handler.CXFServerAuthHandler"/><!--安全日志认证拦截器--><bean id="wss4JInInterceptor" class="org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor"><constructor-arg><map><entry key="action" value="UsernameToken"/><entry key="passwordType" value="PasswordDigest"/><entry key="passwordCallbackRef" value-ref="serverAuthCallback"/></map></constructor-arg></bean>

Implémentation du serveur javax.security Rappel de sécurité fonction de .auth.callback.CallbackHandler :

public class CXFServerAuthHandler implements CallbackHandler {
    protected final static Logger log = LoggerFactory.getLogger(CXFServerAuthHandler.class);
    private static final Map<String, String> userMap = new HashMap<String, String>();

    static {
        userMap.put("webappA", "webappA2017");
        userMap.put("webappB", "webappB2017");
    }

    public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
        for (Callback callback : callbacks) {
            WSPasswordCallback pc = (WSPasswordCallback) callback;

            String clientUsername = pc.getIdentifier();
            String serverPassword = userMap.get(clientUsername);
            log.info(" client:{} is starting webservice...", clientUsername);
            int usage = pc.getUsage();
            if (usage == WSPasswordCallback.USERNAME_TOKEN) {
                pc.setPassword(serverPassword);
            } else if (usage == WSPasswordCallback.SIGNATURE) {
                pc.setPassword(serverPassword);
            }
        }
    }
}

b. Intégrer la configuration du client Spring

    <!--客户端安全认证回调函数--><bean id="wsClientAuthHandler" class="com.rambo.dsc.handler.WsClientAuthHandler"/><!--安全认证对外拦截器--><bean id="wss4JOutInterceptor" class="org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor"><constructor-arg><map><entry key="action" value="UsernameToken"/><entry key="user" value="webappA"/><entry key="passwordType" value="PasswordDigest"/><entry key="passwordCallbackRef" value-ref="wsClientAuthHandler"/></map></constructor-arg></bean>

Intercepteur de configuration du service webService injecté :

        <jaxws:outInterceptors><ref bean="wss4JOutInterceptor"/></jaxws:outInterceptors>

Implémentation du client javax Fonction de rappel de sécurité de .security. auth.callback.CallbackHandler :

public class WsClientAuthHandler implements CallbackHandler {

    public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
        for (Callback callback : callbacks) {
            WSPasswordCallback pc = (WSPasswordCallback) callback;
            pc.setPassword("webappA2017");
        }
    }
}

c. Encodage sans client intégré Spring

  private void addWSS4JOutInterceptor(Object wsClass) {
        Endpoint cxfEndpoint = ClientProxy.getClient(wsClass).getEndpoint();
        Map outProps = new HashMap();
        outProps.put(WSHandlerConstants.ACTION, WSHandlerConstants.USERNAME_TOKEN);
        outProps.put(WSHandlerConstants.USER,"webappA");
        outProps.put(WSHandlerConstants.MUST_UNDERSTAND, "0");
        outProps.put(WSHandlerConstants.PASSWORD_TYPE, "PasswordDigest");
        outProps.put(WSHandlerConstants.PW_CALLBACK_CLASS, WsClientAuthHandler.class.getName());
        cxfEndpoint.getOutInterceptors().add(new WSS4JOutInterceptor(outProps));
    }

L'authentification de sécurité côté serveur dans le projet utilise UsernameToken. cxf prend en charge de nombreuses méthodes d'authentification/types de mots de passe. Bien sûr, vous pouvez également personnaliser la méthode d'authentification de sécurité.

4. Conclusion

L'architecture de service des sociétés Internet est du sang et des habitudes. Chaque entreprise a ses propres routines et structures. Différent, mais les concepts de base sont les mêmes.

Cette pratique ressemble un peu aux microservices, mais elle est loin d'être suffisante, comme l'enregistrement/le routage des services/la tolérance aux pannes/la mise en cache... il y en a beaucoup, beaucoup, et le projet a été open source, si vous souhaitez le perfectionner ensemble.

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn