spring cloud微服务架构(一):服务的注册与相互调用

1 写在前面

之前在做的业务系统,大概有10多个大业务模块,每个大业务模块下面差多有接近20个小业务模块,代码量惊人。

这还不是重点,因为我司是做定制化软件的,根据客户具体的业务需求开发不同的系统,每当要开发一个新系统前,就从已经开发过的系统中选择一个业务接近的系统拷贝一份,然后进行修改。假如A客户需要的业务模块是a、b、c、d、e,新客户B需要的业务模块是a、c、e、f、g,我们只需要把A客户的系统删除b、d模块,新增f模块就可以了,但是这样就会出现一个问题,A、B两个客户的系统存在严重的代码耦合(耦合a、c、e模块)。

有同事提出了解决方案,“我们做一个标准版的系统吧,包括a、b、c、d、e、f、g、...所有的业务模块!”这就是我司目前的做法。看似是一个不错的想法,但是把所有模块都集中在一个系统中,导致war包过大,代码过多,运维困难,一个错误可能导致系统无法运行。

我一直在思考,能不能把业务模块单独拿出来做成系统,单独部署,假如又有新客户C需要的业务模块是c、d、f,那么我们就可以从中选择这几个系统进行组合,能够达到组合的效果。这就是我学习spring could的动机(虽然我给公司这样建议了没被采纳!)。

image.png

在我的方案中,主要解决两个问题:

1)模块之间的耦合
2)模块的复用

2 微服务架构原理

2.1 传统开发模式——单应用架构

先来看一下传统的开发模式,也就是现在我司的开发模式:所有的模块都打包在一个war包里,部署在tomcat里,基本没有外部依赖,这就导致了很多缺点,例如:

1)效率低:开发都在同一个项目改代码,相互等待,冲突不断
2)维护难:代码功功能耦合在一起,新人不知道何从下手
3)不灵活:构建时间长,任何小修改都要重构整个项目,耗时
4)稳定性差:一个微小的问题,都可能导致整个应用挂掉
5)扩展性不够:无法满足高并发下的业务需求

image.png

2.2 微服务架构——多应用架构

微服务架构是将一个大型复杂软件分成多个子软件,或者称为子服务。各个子软件可被独立部署,这样就解决了模块之间耦合的问题,每一个子软件仅仅关注于完成一件任务,并提供服务,不去关注其他业务模块。模块与模块之间只通过接口进行关联,只关注接口,开发人员只对接口负责。
优点可以总结为:

1)每个微服务都很小,这样能聚焦一个指定的业务功能或业务需求
2)一个团队的新成员能够更快投入生产
3)微服务只是业务逻辑的代码,不会和HTML,CSS 或其他界面组件混合
4)每个微服务都有自己的存储能力,可以有自己的数据库。也可以有统一数据库

image.png

3 spring-cloud-Netflix-Eureka实现多服务注册及服务之间的相互调用

spring cloud提供了一个服务注册管理中心Eureka,每一个服务(子软件)都可将自己提供的服务在注册中心登记,如果想要寻找某一个服务,在服务注册中心就能够轻松的找到。以点买卖为例:饿了吗客户端就相当于一个餐厅注册中心,客户通过饿了吗查找特定的餐厅(寻找服务),每个餐厅提供一种服务。

3.1 创建服务注册中心

spring cloud是基于spring boot的,所以我们创建一个spring boot项目,命名为elema-eureka,pom文件如下:

<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.2.RELEASE</version>
        <relativePath /> <!-- lookup parent from repository -->
    </parent>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Dalston.SR3</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-config</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka-server</artifactId>
        </dependency>
    </dependencies>

在src/main/resources目录下新建配置文件application.yml,并在其中配置启动端口,有一点需要特别注意,其实eureka服务注册中心其实也是一个服务,默认情况下,eureka会把自己当成一个服务去注册,我们可以在配置文件中禁止这个行为,application.yml文件内容如下:

server:
  port: 8761
eureka:
  client:
    register-with-eureka: false
    fetch-registry: false

接下来创建spring boot项目启动类,ServerApp.java,使用@EnableEurekaServer注解把该项目标记为eureka注册中心,内容如下:

@SpringBootApplication
@EnableEurekaServer
public class ServerApp {

    public static void main(String[] args) {
        new SpringApplicationBuilder(ServerApp.class).web(true).run(args);
    }

}

启动项目后,我们就可以访问eureka服务中心可视化界面了。localhost:8761,能够在该页面中清楚的看到哪些服务已经注册了。


image.png

3.2 创建服务,注册服务

同样是先创建一个spring boot项目,命名为restaurantA,pom文件内容如下:

<dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Dalston.SR3</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-config</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
        </dependency>
    </dependencies>

在src/main/resources目录下新建配置文件application.yml,并在其中配置启动端口,服务名称,及服务注册地址。服务注册地址就写eureka的地址即可,内容如下:

spring:
  application:
    name: restaurantA
server:
  port: 8081
eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/

然后编写一个服务提供类,命名为RestaurantController.java,并使用@RestController注解向外提供服务,内容如下:

@RestController
public class RestaurantController {

    @RequestMapping(value = "/call", method = RequestMethod.GET,
            produces = MediaType.APPLICATION_JSON_VALUE)
    public String call() {
        return "restaurantA is open!";
    }
}

编写启动类后,然后启动,我们发现在eureka服务中心多了一个名为restaurantA的服务。


image.png

3.3 创建用户,调用restaurantA服务

有很多博客区分了服务提供者和服务使用者,其实在某种意义上来说,服务提供者和使用者是没有明确界限的,一个项目既可以做服务提供者也可以做使用者。

同样创建一个spring boot项目,pom文件内容如下,增加了对JSP支持的依赖:

<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.2.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.7</java.version>
    </properties>
    
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Dalston.SR3</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-config</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-ribbon</artifactId>
        </dependency>
        
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
        
        <!-- JSP support -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
        
        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-jasper</artifactId>
        </dependency>
        
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
        </dependency>
       
    </dependencies>

配置文件application.yml,虽然userA不向外提供服务,但仍然可以向eureka注册,其实也为了能够调用在eureka上注册的其他服务。同时也增加了对JSP文件的支持,可参考spring mvc的配置,内容如下:

spring:
  application:
    name: userA
  mvc:
    view:
      prefix: /WEB-INF/jsp/
      suffix: .jsp
  http:
    encoding:
      force: true
      charset: UTF-8
      enabled: true
server:
  port: 8089
eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/

下面编写订餐OrderController.java,并提供JSP订餐页面,代码如下:

@Controller
@Configuration
public class OrderController {
    
    @Bean
    @LoadBalanced
    public RestTemplate getRestTemplate() {
        return new RestTemplate();
    }

    /**
     * 跳转到订餐页面
     * @param request
     * @return
     */
    @RequestMapping("/toPage")
    public String toList(HttpServletRequest request){
        return "page/orderDinner/list";
    }
    
    @GetMapping("/order")
    @ResponseBody
    public String router() {
        RestTemplate tpl = getRestTemplate();
        String result = tpl.getForObject("http://restaurantA/call", String.class);
        return result;
    }

}

编写启动类,然后启动,访问订餐页面:http://localhost:8089/toPage,然后点击“调用订餐按钮”即可调用restaurantA提供的服务!

image.png

image.png

3.4 小节

上面一共创建了3个项目, elema-eureka:服务注册中心, restaurantA、userA:服务提供者,三者之间的关系如下:

image.png

参考:

  1. 微服务架构设计
  2. 微服务架构
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342