회사 비즈니스의 지속적인 변화에 따라 몇 년 전 프로젝트 A와 기본 DB_A 데이터베이스가 핵심 비즈니스 서비스와 핵심 데이터베이스로 전환되었습니다.
DB_A 데이터베이스에서 데이터를 얻으려는 웹 서비스가 점점 더 많아지고 프로젝트 간의 관계는 점차 다음과 같이 발전했습니다.
개발 추세를 쉽게 볼 수 있습니다. 위 그림처럼 문제가 많습니다(프로젝트 관계는 개인이 추상화한 단순화된 버전이므로 실제 상황은 이보다 훨씬 더 복잡합니다).
a. webappA 실행 중 예외가 발생하여 접속할 수 없는 경우 webappB/webappC .... DB_A 데이터를 정상적으로 가져올 수 있나요?
b. webappB/webappC에 제공되는 다양한 서비스... DB_A 데이터를 얻기 위해 webappA의 양은 수평적으로 무한히 확장될 것입니다.
c. 실행 과정에서 webappA 프로젝트는 사용자에게 자체 서비스를 정상적으로 제공할 뿐만 아니라 데이터를 얻기 위한 다른 프로젝트의 요청도 고려해야 하며 이로 인해 필연적으로 성능 병목 현상이 발생합니다.
이러한 문제 중 일부는 프로젝트의 온라인 진행 중에 이미 나타났습니다. 때때로 유지 관리가 중단되어 프로젝트 팀의 면전을 크게 때리는 것은 정말 불편합니다.
여담: 현재 인터넷의 발전 속도와 다양한 기업의 사업 확장에 따라 2년 안에 프로젝트의 발전 방향을 정확하게 예측하고/미리 확장을 준비할 수 있는 건축가는 이미 매우 훌륭합니다.
프로젝트 팀원 중 누군가가 프로젝트 webappA를 우회하자고 제안했고, 나머지 webappB/webappC는... 상호 작용을 위해 DB_A에 직접 연결했지만 빠르게 거부되었습니다(프로젝트마다 데이터베이스 액세스 레이어를 재정의하고 작성해야 함). ).
데이터베이스 접근 레이어를 분리하여 승인된 항목에 접근할 수 있는 서비스로 사용할 수 있나요? 다음과 같습니다:
핵심 아이디어는 무제한의 N wabapp에 대해 특정 데이터베이스에 대한 액세스를 제공하는 것입니다. 이는 프로젝트 간의 결합을 방지할 뿐만 아니라 데이터 액세스 계층의 재사용률도 향상시킵니다.
이미 아이디어가 있으니 시작해 보세요. BB는 문제를 해결할 수 없습니다. 만들고 수많은 구멍을 메우는 데 이틀 정도 걸렸고, 드디어 예상했던 대로 딱 맞았습니다.
원본 프로젝트는 상업적 사용으로 인해 오픈소스화할 수 없습니다. 데모를 재구성한 후 다음 위치에 오픈소스화했습니다.
이 분야에서 실습이 필요한 학생들의 경우 로컬에서 복제하고 실행하면 모든 것이 명확해집니다.
dao-service-impl 서비스에 액세스하려면 dap-service-api에 의존하는 DB_A 데이터 프로젝트가 필요합니다.
dao-service-api는 외부 레이어에 제공되는 인터페이스입니다. 최종 프레젠테이션 방법은 jar이며, Maven 프로젝트는 이에 직접 의존할 수 있습니다.
maven이 아닌 오래된 프로젝트가 있는 경우 maven-jar-plugin/maven-assemblies-plugin을 사용하여 모든 종속 jar을 어셈블하고 프로젝트 lib에 추가하세요.
dao-service-impl은 cxf + spring + druid + jpa(hibernate impl) 오픈 소스 클래스 라이브러리로 구축된 순수 백엔드 구성 요소 서비스입니다.
서비스 인터페이스의 구현 레이어로서 최종 프리젠테이션 방법은 war이며 클러스터로 배포하거나 배포하여 다른 프로젝트에 서비스를 제공할 수 있습니다.
디렉토리 구조가 한눈에 명확하고, 개발 시작이 매우 빠릅니다. 간단한 코드 생성(GenCodeServlet)을 구현했으며, dao 레이어 + webService 레이어 인터페이스 및 구현을 자동으로 생성할 수 있습니다.
webService는 레이어 주입 dao 레이어 인터페이스를 구현하고 단일 테이블을 추가, 삭제, 수정 및 확인하는 5가지 방법을 캡슐화합니다. 일반적으로 중복된 방법을 작성할 필요가 없으며 SQL의 90% 작성을 피할 수 있습니다.
@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); }
개발 방법은 간단하고 투박합니다. 도구를 사용하여 최대 절전 모드 데이터베이스 po를 역으로 생성하고 GenCodeServlet에 액세스하여 dao/ws 계층 인터페이스 및 구현을 생성합니다.
구성 파일 옵션을 추가하고 cxf webService 서비스를 게시하는 데는 5분 이상 걸리지 않습니다.
게시된 단일 테이블 서비스는 비즈니스 논리를 결합하기 위해 프로젝트에서 지정한 위치에 주입되는 호출자의 데이터베이스 액세스 계층으로 이해됩니다.
이 모듈의 목적은 cxf에서 게시한 서비스를 통합하는 방법을 시연하는 것입니다. ㅋㅋㅋ
b. Spring은 호출자 프로젝트에 통합되지 않습니다(dao-service-api에 따라 다름).
도구나 명령을 사용하여 cxf 서비스 클라이언트를 생성하고, 사용되는 서비스 인스턴스를 얻기 위해 팩토리 모드를 도입하고, 결합합니다. 그게 다야.
<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>
4. cxf 보안 인증 메커니즘
보안 인증이 cxf ws-security wss4j 인터셉터 구현에 도입되어 비누 헤더에 인증 정보가 추가됩니다.
a. 서버 구성
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;
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());
b. Spring 클라이언트 구성
<!--服务端安全认证回调函数--><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>
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); } } } }
클라이언트는 javax.security.auth.callback.CallbackHandler의 보안 콜백 기능을 구현합니다.
<!--客户端安全认证回调函数--><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>
c .인코딩 Spring 통합이 없는 클라이언트
<jaxws:outInterceptors><ref bean="wss4JOutInterceptor"/></jaxws:outInterceptors>
프로젝트의 서버 측 보안 인증은 UsernameToken을 사용합니다. cxf는 다양한 인증 방법/비밀번호 유형을 지원합니다. 물론 보안 인증 방법을 사용자 정의할 수도 있습니다.
4. 결론인터넷 기업의 서비스 아키텍처는 혈통과 습관입니다. 각 기업마다 루틴과 구조가 있고 세부 사항은 다르지만 핵심 개념은 동일합니다.
이 연습에는 약간의 마이크로서비스 느낌이 있지만 서비스 등록/라우팅/내결함성/캐싱 등 충분하지 않습니다. 프로젝트가 너무 많습니다. 오픈소스로 제공됩니다. 관심이 있으시면 함께 개선해 보세요.
위 내용은 독립적인 데이터베이스 액세스를 위한 중간 서비스 생성의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!