Create an intermediate service for independent database access and create independent database access
As the company's business continues to change, A's Project A and the underlying DB_A database turned to be the core business service and core database A few years ago.
There are more and more web services that want to get data from DB_A, and the relationship between projects gradually evolves into the following:
It is easy to see that there will be many problems according to this development trend (project relationships are simple versions abstracted by individuals, and the actual situation is much more complicated than this ).
A. When an exception occurs during webappA operation, can the DB_A data be normally obtained from webappB/webappC?
B. All kinds of services that are provided to webappB/webappC... to obtain DB_A data are concentrated in webappA. The volume of webappA will expand infinitely horizontally. No one would like to peat them, right?
C. In addition to providing services to users during the running process of the webappA project, it also takes into account requests from other projects to obtain data, which will inevitably lead to performance bottlenecks.
Some of these problems have already occurred in the project launch process. The downtime and maintenance work has become a resounding slap in the face of the project team.
Digress: according to the current speed of Internet development and business expansion of various companies, architects who can accurately predict the development direction of the project within two years/and do a good job of expansion in advance are already very good.
Someone in the project team suggested bypassing the project webappA, and other webappB/webappC... directly connect to DB_A for interaction, but it is quickly rejected (you must redefine and write the database access layer for each project ).
Can I separate the database access layer as a service that allows authorized project access? As follows:
The core idea is to provide access to specific databases for an infinitely increasing number of wabapps. This not only avoids coupling between projects, but also improves the Reuse Rate of the data access layer.
If you already have an idea, start it. BB cannot solve the problem. It took about two days to build and fill in countless pitfalls. The final pitfalls are exactly the same as I expected.
The original project was not open-source for commercial use. After the demo was reorganized, it was already open-source: http://git.oschina.net/lanboex/dao-service-demo.
If you need this practice, it is clear that everything is done when you clone it to the local machine.
1. Service Interface Layer
DB_A Data project must rely on dap-service-api to access dao-service-impl service.
Dao-service-api is the interface provided to the outer layer, and the final rendering method is jar. maven projects can directly rely on it.
If an old non-maven project exists, use maven-jar-plugin/maven-assembly-plugin to assemble all the dependent jar files and add them to the Project lib.
2. Service Implementation Layer
Dao-service-impl is a pure backend component service built by the cxf + spring + druid + jpa (hibernate impl) Open Source class library.
As the Implementation Layer of the service interface, the final presentation mode is war. You can perform cluster or distributed deployment to provide services to other projects.
The directory structure is clear at a glance, and the development speed is very fast. The simple code generation (GenCodeServlet) is implemented by myself, and the dao layer + webService layer interfaces and implementations can be automatically generated.
WebSevice implements layer-based injection to the dao layer interface. It adds, deletes, modifies, and queries five methods for a single table encapsulation. In general, no redundant methods are required to avoid writing 90-percent SQL statements.
@ WebService @ SOAPBinding (style = SOAPBinding. style. RPC) public interface UserWs {/*** objects returned by a single User object through ID * cxf transmission cannot be null. When the Dao layer gets null, * an empty object is returned for instantiation, when determining the null value, you can use the object primary key to determine ** @ param id Primary Key ID */UserPO getUser (String id ); /*** obtain multiple User object objects through a similar PO ** @ param userPO object comparison */List <UserPO> listUser (UserPO userPO ); /*** use a similar PO to obtain multiple User object objects ** @ param userPO object comparison * @ param orderby sorting field * @ param asc whether to ascending */List <userPO> listUserOrdrBy (UserPO userPO, string orderby, Boolean asc);/*** add User object ** @ param userPO the object to be added */UserPO addUser (UserPO userPO ); /*** update the User object ** @ param userPO the object to be updated */UserPO updateUser (UserPO userPO );}
The development method is simple and crude. The tool is used to generate the hibernate database po in reverse order and access GenCodeServlet to generate the dao/ws layer interface and implementation.
Add the configuration file option and publish the cxf webService. It is estimated that no configuration file is required in 5 minutes.
3. Service caller
The released single-table service is understood as the database access layer in the caller, injecting it in the place specified by your project, and coupling the processing business logic.
The meaning of this module is equivalent to a demo of how to integrate the Service released by cxf.
A. the caller's project has integrated spring (relying on 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>
Specific usage (on the premise of spring injection)
Map <String, Object> map = new HashMap <> (); UserWs userWs = (UserWs) SpringContextUtil. getBean ("UserWs"); UserPO user = userWs. getUser ("031e7a%72e11e6acede16e%1c0fe"); map. put ("1. obtain a single user: ", user); user. setPhone ("18975468245"); UserPO userPO1 = userWs. updateUser (user); map. put ("2. update a single user: ", userPO1); UserPO userPO2 = new UserPO (); userPO2.setName (" rambo "); userPO2.setPasswd (SecurityUtil. encryptMD5 ("123456"); userPO2.setSex ("male"); userPO2.setYxbz ("Y"); UserPO userPO3 = userWs. addUser (userPO2); map. put ("3. add a single user: ", userPO3); UserPO userPO4 = new UserPO (); userPO4.setSex (" male "); List <UserPO> userPOList = userWs. listUser (userPO4); map. put ("4. obtain all male users: ", userPOList); UserPO userPO5 = new UserPO (); userPO5.setSex (" male "); List <UserPO> userPOList1 = userWs. listUserOrdrBy (userPO5, "sorts", true); map. put ("5. obtain all male users and sort them by sorts field: ", userPOList1); return map;
B. spring is not integrated in the caller's Project (relying on dao-service-api)
Use tools or commands to generate the cxf service client, introduce the factory mode, and obtain the service instance for coupling.
UserWsImplService userWsImplService = new UserWsImplService (new URL (cxfServerUrl + "/UserWs? Wsdl "); UserWs userWs = userWsImplService. getUserWsImplPort (); addWSS4JOutInterceptor (userWs); UserPO user = userWs. getUser ("031e7a%72e11e6acede16e%1c0fe"); map. put ("1. obtain a single user: ", user); user. setPhone ("18975468245"); UserPO userPO1 = userWs. updateUser (user); map. put ("2. update a single user: ", userPO1); UserPO userPO2 = new UserPO (); userPO2.setUuid (StringUtil. getUUID (); userPO2.setName ("rambo"); userPO2.setPasswd (SecurityUtil. encryptMD5 ("123456"); userPO2.setSex ("male"); userPO2.setYxbz ("Y"); UserPO userPO3 = userWs. addUser (userPO2); map. put ("3. add a single user: ", userPO3); UserPO userPO4 = new UserPO (); userPO4.setSex (" male "); UserPOArray userPOArray1 = userWs. listUser (userPO4); map. put ("4. obtain all male users: ", userPOArray1); UserPO userPO5 = new UserPO (); userPO5.setSex (" male "); UserPOArray userPOArray2 = userWs. listUserOrdrBy (userPO5, "sorts", true); map. put ("5. obtain all male users and sort them by sorts field: ", userPOArray2.getItem ());
4. cxf Security Authentication Mechanism
Cxf adopts the soap communication protocol. After all, it is a service released externally, and security is very important.
Security authentication is implemented by introducing the cxf ws-security wss4j interceptor and adding authentication information to the soap message header.
A. Server Configuration
<! -- Server Security Authentication callback function --> <bean id = "serverAuthCallback" class = "com. rambo. dsd. base. handler. CXFServerAuthHandler"/> <! -- Security Log authentication interceptor --> <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>
The server implements the security callback function 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. Integrate Spring client Configuration
<! -- Client Security Authentication callback function --> <bean id = "wsClientAuthHandler" class = "com. rambo. dsc. handler. WsClientAuthHandler"/> <! -- Security Authentication blocker --> <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>
Configure the interceptor for the injected webService:
<jaxws:outInterceptors> <ref bean="wss4JOutInterceptor"/> </jaxws:outInterceptors>
The client implements the security callback function of 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. coding on clients not integrated with 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)); }
In the project, Server Security Authentication uses UsernameToken. cxf supports many authentication methods and password types. Of course, you can also customize security authentication methods.
4. Conclusion
Internet companies' service architecture is a blood and habit. Every company has its own routines and architecture. The details are different, but the core concept is the best.
This practice is somewhat microservices, but it is far from enough, such as service registration, routing, fault tolerance, and caching ..... there are a lot of projects that are open-source. If you want to improve them together.