您好,欢迎来到三六零分类信息网!老站,搜索引擎当天收录,欢迎发信息
免费发信息
三六零分类信息网 > 潍坊分类信息网,免费分类信息发布

打造独立数据库访问的中间服务

2024/5/6 22:49:40发布9次查看
随着公司业务的不断变化,几年前的 a 项目和底层 db_a 数据库华丽转身为核心业务服务和核心数据库。
   想从  db_a  数据库获取数据的 web 服务越来越多,项目之间的关系逐渐演变为下面这样:
很容易看出来按上图这样的发展趋势会存在很多问题(项目关系为个人抽象出来的简化版,实际情况比这要复杂的多)。
   a. 当 webappa 运行过程中出现异常无法访问,webappb/ webappc .... 还能正常获取  db_a 数据吗?
   b. 各种各样的提供给 webappb/webappc ... 获取 db_a 数据的服务都集中在 webappa 中,webappa 的体积会无限水平扩张,谁都不喜欢赘肉对吧?
   c. webappa  项目在运行过程中除了要正常提供自己的服务给用户以外,还要兼顾其他项目获取数据的请求,势必会造成性能瓶颈。
   其中的有些问题已经在项目上线推进中出现过,隔三差五停机维护变成响亮的巴掌扇到项目组的脸上确实也不好受。
   题外话:按照目前互联网的发展速度和各公司业务扩展,能准确预测项目两年以内发展方向/并提前做好扩展的架构师,能力已经非常不错。
   项目组有人提出绕开项目 webappa ,其余的 webappb/webappc ...直接连接 db_a 进行交互,但很快被否决了(每个项目数据库访问层你都要重新定义和编写)。
   能否将其中的数据库访问层独立出来,做为服务容许授权的项目进行访问?如下:
核心想法是为无限增多的n个 wabapp 提供特定数据库的访问。这样既避免了项目之间的耦合,也提高的数据访问层的重用率。
   想法已经有了,那就开干吧,bb 解决不了问题。大概花了两天时间进行搭建,填了无数坑,终于出落的和我预想中的一样贴切。
   原项目因商用无法开源,demo 经我重新组织已开源到:。
   需要这方面实践的同学,clone 到本地跑起来一切也就明朗了。
1. 服务接口层   
   需要 db_a 数据项目依赖 dap-service-api 访问 dao-service-impl 服务即可。
   dao-service-api 为提供给外层的接口,最终的呈现方式为 jar, maven 项目直接依赖即可。
如果存在老旧非 maven 项目,使用 maven-jar-plugin/maven-assembly-plugin 将所依赖的 jar 都装配进去添加到项目 lib 里面。
2. 服务实现层   dao-service-impl 由 cxf + spring + druid + jpa(hibernate impl) 开源类库搭建而成的纯后端组件服务。
做为服务接口的实现层,最终呈现方式为 war,可进行集群或分布式部署,给其他项目提供服务。
   目录结构一目了然,上手开发速度很快,其中自己实现了简易的代码生成(gencodeservlet),dao 层 + webservice 层 接口和实现都可以自动生成。
   websevice 实现层注入 dao 层接口,针对单表封装增删改查5个方法,大体上不用写多余的方法,避免编写百分之 90 的 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); }
开发方式简单粗暴,使用工具反向生成 hibernate 数据库 po ,访问 gencodeservlet 生成 dao/ws 层接口和实现。
   添加配置文件选项,发布 cxf webservice 服务即可,估计5分钟的时间都不要。
3. 服务调用方   发布的单表服务在调用方里面理解为数据库访问层,在你项目规定的地方注入,进行耦合处理业务逻辑。
   这个模块存在的意义,相当于一个怎样集成 cxf 发布的服务的 demo。
a.调用方项目中已集成了 spring (依赖 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>
具体的使用方式(在 spring 注入的前提下)
        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 (依赖 dao-service-api)
   使用工具或者命令生成 cxf 服务客户端,引入工厂模式在使用的地方获取服务实例,进行耦合即可。
            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. cxf 安全认证机制   cxf 采用 soap 通信协议,毕竟是对外发布出去的服务,安全性还是很重要。
   安全认证引入 cxf ws-security wss4j  拦截器实现,soap 报文头添加认证信息。
   a.服务端配置
   <!--服务端安全认证回调函数--><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>
服务端实现 javax.security.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.集成 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>
注入的 webservice 服务配置拦截器:
        <jaxws:outinterceptors><ref bean="wss4joutinterceptor"/></jaxws:outinterceptors>
客户端实现 javax.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.未集成 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));     }
项目中服务端安全认证使用的是 usernametoken,cxf 支持认证方式/密码类型还有很多,当然你也可以自定义安全认证方式。
 4.结束语
   互联网公司服务架构是血液,是习惯,每家公司都有自己的套路和架构,细节有不同,但是核心理念是通的。
   这次实践有点微服务的感觉,但还远远不够,如服务的注册/路由/容错/缓存.....很多很多,项目已开源到上面,有兴趣一起完善它吧。
以上就是打造独立数据库访问的中间服务的详细内容。
潍坊分类信息网,免费分类信息发布

VIP推荐

免费发布信息,免费发布B2B信息网站平台 - 三六零分类信息网 沪ICP备09012988号-2
企业名录