java温故笔记(六)java的SOA架构

Wesley13
• 阅读 1019

SOA(面向服务的架构)

首先Martin Fowler提出SOA歧义Service Oriented Ambiguity,认为"什么是SOA"是不可能回答,因为不同的人意味着不同的事情,SOA意味服务接口,意味流程整合,意味资源再利用,意味着管制,在下面SOA组件图中,服务和服务消费者(客户端)之间存在多个约束,当一个服务显式暴露后,客户端能够通过绑定定位到该服务,相当于两者签订了合同,规定了合同内容和如何实施,具体合同的描述是通过消息方式进行:

java温故笔记(六)java的SOA架构

  由于Java等传统语言主要是以类表达对象,将功能行为封装在类内部,而业务客户一般都是注重软件的功能,包括同行业公司系统之间数据交流也是以功能服务为接口,因此,面向服务的架构SOA更加贴近业务客户,也更适合业务伙伴之间流程整合,各个行业已经诞生自己行业特点的SOA,例如电信联盟的NGOSS,已经成为电信行业业务支撑系统BOSS的标准。SOA不但是技术用语,也是业务销售用语,通过服务这个中间概念,可以实现业务和技术之间的无缝转换,如今SOA已经和REST DDD以及云计算等新技术方法结合。其内部主要概念有SCA ESB JBI等等,涉及工作流 规则引擎 消息总线等多个技术细节方面。

  通常,一个架构师进行系统架构顶层设计时,必须考虑使用者的利益,不能单单实现软件的功能,还要考虑到软件的性能Scalable 可用性available/usable 安全性等软件质量,还要借鉴社区的最佳实践和经验形成的模式和反模式,避免重蹈覆辙和陷阱,再大胆采取最新的软件技术(比如用REST替代SOAP等)。

  服务的提出其实隐含了两个概念,服务提供者和服务消费者,这两者之间有一个合同约定,这非常类似我们现实生活中签订的服务合同,A单位和B单位分别是服务的提供者和消费者,两者签订了一个服务合同,规定A为B提供某项服务。服务就是提供一些公共需求的设施,通过一个工作过程能提供帮助,使用,让使用者受益。

  服务具体有如下:Windows Service:如PC定位者RPC Locator, 事件日志EventLog, DHCP Client,;. 软件服务Software Service,如分布式服务Distribution Service, 警告服务Alert Service 安全服务 Security Service, 日志服务;业务服务Business Service,如 帐号和客户服务,销售服务,订单服务,采购服务。

  服务两个重要特点:自治和管制,自治代表服务不能被外部势力牵制,比如如果一个服务内部处理中需要调用外部资源或等待外部流程结束,这种等待不能影响服务本身的调用,如果一个服务分为显式对外和隐式内部两个部分,那么自治是针对隐式内部,意味着我们不能在具体一个服务中直接使用同步代码实现复杂功能。如:

public AServiceImpl implements AService{
  public void productSale(...){
    Product product = productService.getProduct();
    int inventory = InventoryService.getInventory(product);
    int price = priceService.getPrice(product);
  }
}

  在AServiceImpl的productSale方法中,我们获得商品的库存和定价,都是通过同步的RPC实现调用的,这样造成productSale方法依赖于InventoryService和priceService,无法实现自身自治。

  实现服务真正自治,实际就是解决类之间依赖耦合的问题,消息是一种方式,但是基于消息又有两种通讯方式,基于请求响应和基于事件的EDA。

  从服务自治可以看出,为什么要提出服务必须自治,因为服务是受管制的,在实际业务活动中,不同服务是被不同部分管理,比如定价服务归属财务部门系统,库存归属仓库系统,涉及系统之间调用协调不能自己使用同步RPC,而是需要消息。

  在实际应用中,很多单位使用SOA主要看中其能够无缝整合新旧系统,称为EAI企业应用整合,下图是苏宁的一种SOA图,使用ESB企业服务总线这样的消息系统整合了新旧各种系统。

java温故笔记(六)java的SOA架构

  使用SOA和ESB能够灵活实现业务流程管理,工作流的管理BPM,如下图,一个订单的产生可能需要几个部门批准才能完成,而且这几个部门经常是变化的,如何灵活实现这种批准流程的定制也成为SOA实现的一部分,如下:

java温故笔记(六)java的SOA架构

  注意图中1 2 3 4 5 6 7 8 9标注的订单处理流程步骤,这种不同服务之间调用处理顺序可通过BPM进行灵活定制。

  目前提供SOA全套解决方案和产品的厂商很多,包括IBM SAP和Oracle,国内金蝶用友浪潮软件等等,比如苏宁的SOA是以SAP为主的八国联军组装,既然SOA中间件服务商已经为我们提供了成熟的架构方案和产品,那么作为SOA使用者是否就无需顶层架构设计了呢?当然不是,SOA使用者要根据自己业务进行模块划分,进行领域建模设计,根据DDD领域驱动设计将业务分解为一个上下文模块,然后再用服务作为对外接口,内部封装的是DDD聚合根,而传统SOA作法是内部封装的是数据表的DTO,从而导致SOA服务内部腐烂堵塞,违背SOA自治和可用性等原则约束。具体可见DDD领域驱动设计

SOA的好处

1. 松耦合:由于服务自治,有一定封装边界,服务调用交互是通过发布接口。这意味着应用程序不感兴趣的服务如何被实现。
2.位置透明:服务的消费者不必关系服务位于什么地方。
3.可在异构平台间复用。可以将遗留系统包装成服务。
4.便于测试,能并行开发,较高可靠性和良好可伸缩性。

SOA基础

SOA面试题

SOA案例项目源码

SOA最佳实践

微服务架构

微服务实现工具概述

为什么RESTful微服务和异步编程是一种趋势?

Lagom是一个集成ES/CQRS的Reactive微服务框架

Docker:VM、代码迁移和SOA解决方案

Docker容器化组件架构

微服务专题

EAI集成

SOA之企业应用集成EAI

Apache ServiceMix介绍

什么时候使用Apache camel

SOA集成框架Spring Integration, Mule ESB or Apache Camel比较

Apache Camel建立基于消息的应用

基于Spring集成的EDA的开源项目

各种ESB产品比较

使用REST实现企业集成

使用Apache Camel实现REST端点集成

为什么你并不需要企业服务总线(ESB)

Apache Kafka简单介绍

ESB专题

Web服务

使用Spring Webservices建立SOAP服务代理

六个强大的创建RESTful服务的Java框架

使用Apache CXF开发RESTful服务

使用Apache CXF开发Web服务

使用SCA和JAX-RS建立RESTful服务

ZooKeeper在服务发现中应用

使用zookeeper和curator实现微服务的负载平衡

使用vert.x 2.0, RxJava 和 mongoDB创建一个简单的RESTful服务

Spring RESTful服务源码案例下载

使用Shiro实现基于服务的多域身份验证和授权

REST服务专题

WEB服务WebService专题

微服务架构快速指南

Netflix hystrix入门教程

BPM工作流

在亚马逊云计算平台上使用Camel开发分布式工作流

Effektif:第一个基于云的BPM产品

工作流专题

业务流程的新实现:微服务和事件编排

SOA最佳实践

构建实时流数据平台实践指南

SugarCRM使用EDA替代SOA

SOA并不能解决高并发事务

你的SOA已经使用了EDA和CQRS吗?

OOD vs SOA

DCI和服务Services (EJB)

使用Fabric3实现服务的组合

SOA企业线下培训咨询

-----------------------------

基于 dubbo 的分布式架构

前言

现在越来越多的互联网公司还是将自己公司的项目进行服务化,这确实是今后项目开发的一个趋势,就这个点再凭借之前的SSM项目来让第一次接触的同学能快速上手。

浅谈分布式架构

分布式架构单看这个名字给人的感觉就是高逼格,但其实从历史的角度来分析一下就比较明了了。

我们拿一个电商系统来说:

单系统

java温故笔记(六)java的SOA架构

E65B5547-AF84-4D31-836D-72892C7AC7EA.png

对于一个刚起步的创业公司项目肯定是追求越快完成功能越好,并且用户量也不大。

这时候所有的业务逻辑都是在一个项目中就可以满足。

垂直拆分-多应用

java温故笔记(六)java的SOA架构

QQ20170406-230056@2x.jpg

当业务量和用户量发展到一定地步的时候,这时一般会将应用同时部署到几台服务器上,在用户访问的时候使用Nginx进行反向代理和简单的负载均衡。

SOA服务化

当整个系统以及发展的足够大的时候,比如一个电商系统中存在有:

  • 用户系统
  • 订单系统
  • 支付系统
  • 物流系统

等系统。
如果每次修改了其中一个系统就要重新发布上线的话那么耦合就太严重了。

所以需要将整个项目拆分成若干个独立的应用,可以进行独立的开发上线实现快速迭代。

java温故笔记(六)java的SOA架构

dubbo.png

如上图所示每个应用之间相互独立,每个应用可以消费其他应用暴露出来的服务,同时也对外提供服务。

从架构的层面简单的理解了,接下来看看如何编码实现。

基于dubbo的实现

dubbo应该算是国内使用最多的分布式服务框架,基于此来实现对新入门的同学应该很有帮助。

其中有涉及到安装dubbo服务的注册中心zookeeper等相关知识点可以自行查看官方文档,这里就不单独讲了。

对外提供服务

首先第一步需要在SSM-API模块中定义一个接口,这里就搞了一个用户查询的接口

/**
 * Function:用户API
 * @author chenjiec
 * Date: 2017/4/4 下午9:46
 * @since JDK 1.7
 */
public interface UserInfoApi {

    /**
     * 获取用户信息
     * @param userId
     * @return
     * @throws Exception
     */
    public UserInfoRsp getUserInfo(int userId) throws Exception;
}

接着在SSM-SERVICE模块中进行实现:

import com.alibaba.dubbo.config.annotation.Service;
/**
 * Function:
 * @author chenjiec
 * Date: 2017/4/4 下午9:51
 * @since JDK 1.7
 */
@Service
public class UserInfoApiImpl implements UserInfoApi {
    private static Logger logger = LoggerFactory.getLogger(UserInfoApiImpl.class);

    @Autowired
    private T_userService t_userService ;

    /**
     * 获取用户信息
     *
     * @param userId
     * @return
     * @throws Exception
     */
    @Override
    public UserInfoRsp getUserInfo(int userId) throws Exception {
        logger.info("用户查询Id="+userId);

        //返回对象
        UserInfoRsp userInfoRsp = new UserInfoRsp() ;
        T_user t_user = t_userService.selectByPrimaryKey(userId) ;

        //构建
        buildUserInfoRsp(userInfoRsp,t_user) ;

        return userInfoRsp;
    }


    /**
     * 构建返回
     * @param userInfoRsp
     * @param t_user
     */
    private void buildUserInfoRsp(UserInfoRsp userInfoRsp, T_user t_user) {
        if (t_user ==  null){
            t_user = new T_user() ;
        }
        CommonUtil.setLogValueModelToModel(t_user,userInfoRsp);
    }
}

这些都是通用的代码,但值得注意的一点是这里使用的dubbo框架所提供的[@service](https://my.oschina.net/service)注解。作用是声明需要暴露的服务接口。

再之后就是几个dubbo相关的配置文件了。

spring-dubbo-config.xml

    <dubbo:application name="ssm-service" owner="crossoverJie"
        organization="ssm-crossoverJie" logger="slf4j"/>

    <dubbo:registry id="dubbo-registry" address="zookeeper://192.168.0.188:2181"
        file="/tmp/dubbo.cachr" />

    <dubbo:monitor protocol="registry" />

    <dubbo:protocol name="dubbo" port="20880" />

    <dubbo:provider timeout="15000" retries="0" delay="-1" />

    <dubbo:consumer check="false" timeout="15000" />

其实就是配置我们服务注册的zk地址,以及服务名称、超时时间等配置。

spring-dubbo-provider.xml

<dubbo:annotation package="com.crossoverJie.api.impl" />

这个配置扫描注解包的位置,一般配置到接口实现包即可。

spring-dubbo-consumer.xml

这个是消费者配置项,表明我们需要依赖的其他应用。
这里我们在SSM-BOOT项目中进行配置:

<dubbo:reference id="userInfoApi"
        interface="com.crossoverJie.api.UserInfoApi" />

直接就是配置的刚才我们提供的那个用户查询的接口,这样当我们自己的内部项目需要使用到这个服务只需要依赖SSM-BOOT即可,不需要单独的再去配置consumer。这个我有在上一篇SSM(十) 项目重构-互联网项目的Maven结构中也有提到。

安装管理控制台

还有一个需要做的就是安装管理控制台,这里可以看到我们有多少服务、调用情况是怎么样等作用。

这里我们可以将dubbo的官方源码下载下来,对其中的dubbo-admin模块进行打包,将生成的WAR包放到Tomcat中运行起来即可。

但是需要注意一点的是:
需要将其中的dubbo.properties的zk地址修改为自己的即可。

dubbo.registry.address=zookeeper://127.0.0.1:2181
dubbo.admin.root.password=root
dubbo.admin.guest.password=guest

到时候登陆的话使用root,密码也是root。
使用guest,密码也是guest。

登陆界面如下图:

java温故笔记(六)java的SOA架构

QQ20170407-001924@2x.jpg

其中我们可以看到有两个服务以及注册上去了,但是没有消费者。

消费服务

为了能够更直观的体验到消费服务,我新建了一个项目:
https://github.com/crossoverJie/SSM-CONSUMER

其中在SSM-CONSUMER-API中我也定义了一个接口:

/**
 * Function:薪资API
 * @author chenjiec
 * Date: 2017/4/4 下午9:46
 * @since JDK 1.7
 */
public interface SalaryInfoApi {

    /**
     * 获取薪资
     * @param userId
     * @return
     * @throws Exception
     */
    public SalaryInfoRsp getSalaryInfo(int userId) throws Exception;
}

因为作为消费者的同时我们也对外提供了一个获取薪资的一个服务。

SSM-CONSUMER-SERVICE模块中进行了实现:

/**
 * Function:
 * @author chenjiec
 * Date: 2017/4/4 下午9:51
 * @since JDK 1.7
 */
@Service
public class SalaryInfoApiImpl implements SalaryInfoApi {
    private static Logger logger = LoggerFactory.getLogger(SalaryInfoApiImpl.class);

    @Autowired
    UserInfoApi userInfoApi ;

    /**
     * 获取用户信息
     *
     * @param userId
     * @return
     * @throws Exception
     */
    @Override
    public SalaryInfoRsp getSalaryInfo(int userId) throws Exception {
        logger.info("薪资查询Id="+userId);

        //返回对象
        SalaryInfoRsp salaryInfoRsp = new SalaryInfoRsp() ;
        
        //调用远程服务
        UserInfoRsp userInfo = userInfoApi.getUserInfo(userId);
        
        salaryInfoRsp.setUsername(userInfo.getUserName());

        return salaryInfoRsp;
    }


}

其中就可以直接使用userInfoApi调用之前的个人信息服务。

再调用之前需要注意的有点是,我们只需要依赖SSM-BOOT这个模块即可进行调用,因为SSM-BOOT模块已经为我们配置了消费者之类的操作了:

        <dependency>
            <groupId>com.crossoverJie</groupId>
            <artifactId>SSM-BOOT</artifactId>
        </dependency>

还有一点是在配置SSM-BOOT中的spring-dubbo-cosumer.xml配置文件的时候,路径要和我们初始化spring配置文件时的路径一致:

java温故笔记(六)java的SOA架构

QQ20170407-005850@2x.jpg

    <!-- Spring和mybatis的配置文件 -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath*:spring/*.xml</param-value>
    </context-param>

接下来跑个单测试一下能否调通:

/**
 * Function:
 *
 * @author chenjiec
 *         Date: 2017/4/5 下午10:41
 * @since JDK 1.7
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath*:/spring/*.xml" })
public class SalaryInfoApiImplTest {

    @Autowired
    private SalaryInfoApi salaryInfoApi ;

    @Test
    public void getSalaryInfo() throws Exception {
        SalaryInfoRsp salaryInfo = salaryInfoApi.getSalaryInfo(1);
        System.out.println(JSON.toJSONString(salaryInfo));
    }

}

java温故笔记(六)java的SOA架构

消费者.jpg

消费者

java温故笔记(六)java的SOA架构

提供者.jpg

提供者
可以看到确实是调用成功了的。

接下来将消费者项目也同时启动在来观察管理控制台有什么不一样:

java温故笔记(六)java的SOA架构

QQ20170407-003413@2x.jpg

会看到多了一个消费者所提供的服务com.crossoverjie.consumer.api.SalaryInfoApi,同时
com.crossoverJie.api.UserInfoApi服务已经正常,说明已经有消费者了。

java温故笔记(六)java的SOA架构

QQ20170407-003456@2x.jpg

点进去便可查看具体的消费者。

总结

这样一个基于dubbo的分布式服务已经讲的差不多了,在实际的开发中我们便会开发一个大系统中的某一个子应用,这样就算一个子应用出问题了也不会影响到整个大的项目。

再提一点:
在实际的生产环境一般同一个服务我们都会有一个master,slave的主从服务,这样在上线的过程中不至于整个应用出现无法使用的尴尬情况。

谈到了SOA的好处,那么自然也有相对于传统模式的不方便之处:

  • 拆分一个大的项目为成百上千的子应用就不可能手动上线了,即需要自动化的部署上线,如Jenkins
  • 还有一个需要做到的就是监控,需要一个单独的监控平台来帮我们实时查看各个服务的运行情况以便于及时定位和解决问题。
  • 日志查看分析,拆分之后不可能再去每台服务器上查看日志,需要一个单独的日志查看分析工具如elk
点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Jacquelyn38 Jacquelyn38
2年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Wesley13 Wesley13
2年前
Java获得今日零时零分零秒的时间(Date型)
publicDatezeroTime()throwsParseException{    DatetimenewDate();    SimpleDateFormatsimpnewSimpleDateFormat("yyyyMMdd00:00:00");    SimpleDateFormatsimp2newS
Wesley13 Wesley13
2年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
2年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
2年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Stella981 Stella981
2年前
Docker 部署SpringBoot项目不香吗?
  公众号改版后文章乱序推荐,希望你可以点击上方“Java进阶架构师”,点击右上角,将我们设为★“星标”!这样才不会错过每日进阶架构文章呀。  !(http://dingyue.ws.126.net/2020/0920/b00fbfc7j00qgy5xy002kd200qo00hsg00it00cj.jpg)  2
Wesley13 Wesley13
2年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
3个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这