微服务实战之春云与刀客(三)—— 面向接口调用代码结构实例

本文涉及的产品
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
云原生网关 MSE Higress,422元/月
注册配置 MSE Nacos/ZooKeeper,118元/月
简介: 概述 在上一篇中提到了spring cloud 面向接口调用的开发风格,这一篇会举一个简单的但完整的例子来说明整个代码结构。代码已上传到 https://github.com/maruixiang/spring-cloud-demo/tree/master/demo1 代码结构 整个代码在demo1目录下面,包含了一个根级的parent pom文件和三个maven应用。

概述

在上一篇中提到了spring cloud 面向接口调用的开发风格,这一篇会举一个简单的但完整的例子来说明整个代码结构。
代码已上传到 https://github.com/maruixiang/spring-cloud-demo/tree/master/demo1

代码结构

image

整个代码在demo1目录下面,包含了一个根级的parent pom文件和三个maven应用。

  • 根pom文件:所有应用的parent pom文件。
  • eureka :注册中心,用于服务注册和发现。
  • demo :demo微服务,它本身包含了两个模块,demo-api提供给第三方使用的接口;demo-service是服务的具体实现。
    为什么每个微服务多加了一层目录结构呢?因为从微服务职责划分来说,不同团队负责不同的微服务开发,每个微服务独立成项目,代码可以随时分离,不混合在一起。
  • client : 客户端调用服务。
    每个应用maven代码结构可以通过http://start.spring.io/ 、IDE工具或手动生成,这里不多述。

统一的根级的pom.xml

  • 这个文件是手工创建的,把spring boot基本信息都统一移到这里面来定义。
    spring-boot 1.5.7.RELEASE
    spring-cloud Dalston.SR3 。
  • 为了方便测试,我把三个应用的modules定义也放入了这里。而实际项目开发中,这个文件是不应该包含modules(实际开发中,这是一个基本不会修改的公共pom文件,不可能新加的微服务都放来来。正确做法是去掉modules,加入发布节点distributionManagement信息,把这个pom 通过mvn deploy 到maven 服务器上供所有的微服务使用即可)。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.cehome.cloud</groupId>
    <artifactId>cehome-cloud-parent</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <packaging>pom</packaging>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.7.RELEASE</version>
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

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


    <build>
        <finalName>${project.artifactId}</finalName>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
    <modules>
        <module>eureka</module>
        <module>demo</module>
        <module>client</module>
    </modules>
</project>

eureka注册中心

读音[ ju:`rika:]

定义pom

继承spring-cloud-parent ,引入依赖 spring-cloud-starter-eureka-server即可

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>com.cehome.cloud</groupId>
        <artifactId>spring-cloud-parent</artifactId>
        <version>1.0.0-SNAPSHOT</version>
        <relativePath>../pom.xml</relativePath>
    </parent>
    <groupId>com.cehome.cloud</groupId>
    <artifactId>eureka</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>eureka</name>
    <description>Demo project for Spring Boot</description>


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

    </dependencies>

</project>

添加spring配置

tomcat端口使用eureka约定的8761

server.port=8761
spring.application.name=eureka
eureka.client.serviceUrl.defaultZone = http://localhost:8761/eureka/
#是不是client?肯定不是啦。为true时,可以启动,但报异常:Cannot execute request on any known server
eureka.client.fetch-registry=false
#单注册中心设为false,集群是需要设为true以便多eureka互相同步
eureka.client.eureka-with-eureka=false

启动类

package com.cehome.cloud.eureka;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

@EnableEurekaServer
@SpringBootApplication
public class EurekaApplication {

    public static void main(String[] args) {
        SpringApplication.run(EurekaApplication.class, args);
    }
}

启动

运行main 方法,然后访问 http://localhost:8761/

开发demo微服务

demo 本身是包含两个模块的maven应用,一个是api供第三方调用,一个是具体逻辑。

接口模块 demo-api

  • 因为需要用到Feign,所以引入Feign依赖即可。
    在pom文件中,还要引入spring-boot-thin-layout 一个很重要的maven插件。因为spring boot 缺省是编译成fat jar(60m左右),而api是要打成jar包给第三方使用的,如果打成一个spring boot结构的fat jar,第三方肯定无法使用了,所以引入了这个maven插件,会把api打成一个我们普通使用的jar包(可能就10多K),这样才能真正实现接口跟逻辑分离的效果。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>
    <groupId>com.cehome.cloud</groupId>
    <artifactId>demo-api</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <parent>
        <groupId>com.cehome.cloud</groupId>
        <artifactId>demo-parent</artifactId>
   
         <version>1.0.0-SNAPSHOT</version>
        <relativePath>../pom.xml</relativePath>

    </parent>

    <dependencies>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-feign</artifactId>
            <scope>provided</scope>
        </dependency>

    </dependencies>
    <build>
        <plugins>

            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <dependencies>
                    <dependency>
                        <groupId>org.springframework.boot.experimental</groupId>
                        <artifactId>spring-boot-thin-layout</artifactId>
                        <version>1.0.5.RELEASE</version>
                    </dependency>
                </dependencies>
            </plugin>

        </plugins>
    </build>

</project>
  • 定义接口类。接口就只有一个get()方法,访问后返回一个固定字符串。
package com.cehome.cloud.demo.api;

import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.web.bind.annotation.RequestMapping;

@FeignClient(name= "demo-service" )
public interface Demo1Service {

    @RequestMapping("/demo1/get")
    String get();


}
  • 需要继续添加一个包含main()方法的类,这个可以随意定义。编译时spring boot 会取找一个main()方法,没有的话编译会报错。
package com.cehome.cloud.demo.api;

public class DemoAPI {

    public static void main(String[] args) {

    }
}

服务模块demo-service

  • 定义pom.xml。除了spring-cloud-starter-eureka、spring-boot-starter-web、spring-cloud-starter-feign 这几个基本的微服务依赖,还要依赖上面定义的接口demo-api。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.cehome.cloud</groupId>
    <artifactId>demo-service</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>demo-service</name>
    <description>Demo project for Spring Boot</description>

     <parent>
        <groupId>com.cehome.cloud</groupId>
        <artifactId>demo-parent</artifactId>
         <version>1.0.0-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>

    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-feign</artifactId>
        </dependency>

        <dependency>
            <groupId>com.cehome.cloud</groupId>
            <artifactId>demo-api</artifactId>
            <version>1.0.0-SNAPSHOT</version>
        </dependency>

    </dependencies>

</project>
  • 定义配置文件application.properties。 spring.application.name就是service Id,跟上面@Feign注解中的名字一样。
eureka.client.serviceUrl.defaultZone = http://localhost:8761/eureka/
server.port = 8762
spring.application.name = demo-service
  • 实现main方法。
    @EnableEurekaClient 让服务跟eureka注册中心关联,可以作为服务提供者也可以作为消费者。
package com.cehome.cloud.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.feign.EnableFeignClients;
import org.springframework.context.annotation.ComponentScan;

@SpringBootApplication
//-- provider and consumer  service
@EnableEurekaClient
@ComponentScan("com.cehome.cloud.demo")
public class DemoServiceApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoServiceApplication.class);
    }
}
  • 继承Demo1Service 接口,实现服务Demo1ServiceImpl。注意这里的服务实际上是一个Controller,我们为了规范和便于管理,让其继承api中定义的接口Demo1Service ;且Demo1ServiceImpl的包名也是用service,不用controller,这就是一种约定,强调这是一个服务。
package com.cehome.cloud.demo.service;

import com.cehome.cloud.demo.api.Demo1Service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class Demo1ServiceImpl implements Demo1Service {

    @Override
    public String get() {
        return "This is Demo1 Service";
    }
}

客户端调用

  • pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

  
    <parent>
        <groupId>com.cehome.cloud</groupId>
        <artifactId>spring-cloud-parent</artifactId>
        <version>1.0.0-SNAPSHOT</version>
    </parent>

    <modelVersion>4.0.0</modelVersion>
    <groupId>com.cehome.cloud</groupId>
    <artifactId>client</artifactId>
    <packaging>jar</packaging>
  
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-feign</artifactId>
        </dependency>
       
        <dependency>
            <groupId>com.cehome.cloud</groupId>
            <artifactId>demo-api</artifactId>
            <version>1.0.0-SNAPSHOT</version>
        </dependency>
    </dependencies>
</project>
  • application.properties
eureka.client.serviceUrl.defaultZone = http://localhost:8761/eureka/
server.port = 8763
spring.application.name = client-demo
  • spring boot主程序。
    @EnableDiscoveryClient - 仅作为消费者(客户端),如果既作为消费端又同时作为服务提供者,则用@EnableEurekaClient。

@EnableFeignClients - 指定了Feign扫描api接口的范围。Feign扫描到包含@Feign注解的interface,然后会实例化一个spring代理bean,代理各种方法调用并转成URL发送请求。

package com.cehome.cloud.client;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.feign.EnableFeignClients;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
//-- consumer  http://blog.didispace.com/spring-cloud-tips-feign-rpc/
@EnableDiscoveryClient
@EnableFeignClients(basePackages = {"com.cehome.cloud"})
@RestController
@ComponentScan("com.cehome.cloud.client")
public class ClientApplication {
    public static void main(String[] args) {
        SpringApplication springApplication=new SpringApplication(ClientApplication.class);
        ConfigurableApplicationContext applicationContext=springApplication.run(args);

    }
}
  • 调用服务。可以看到,在pom.xml 引入demo-api后,通过
    “@Autowired private Demo1Service demo1Service;” 方式,就像调用本地bean一样调用微服务。用户只要引入api jar包就行,不用关心服务部署在哪台机器,也不用care怎样拼接URL去调用服务,这是不是很符合java面向对象开发的要求?
package com.cehome.cloud.client.controller;

import com.cehome.cloud.demo.api.Demo1Service;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/demo1")
public class Demo1Controller {
    private static final Logger logger = LoggerFactory.getLogger(Demo1Controller.class);

    @Autowired
    private Demo1Service demo1Service;
    @GetMapping("/get")
    public String get() {
        logger.info("begin to get ......");
        return demo1Service.get();
    }

}
目录
相关文章
|
5天前
|
存储 监控 供应链
微服务拆分的 “坑”:实战复盘与避坑指南
本文回顾了从2~3人初创团队到百人技术团队的成长历程,重点讨论了从传统JSP到前后端分离+SpringCloud微服务架构的演变。通过实际案例,总结了微服务拆分过程中常见的两个问题:服务拆分边界不清晰和拆分粒度过细,并提出了优化方案,将11个微服务优化为6个,提高了系统的可维护性和扩展性。
22 0
|
1月前
|
运维 NoSQL Java
后端架构演进:微服务架构的优缺点与实战案例分析
【10月更文挑战第28天】本文探讨了微服务架构与单体架构的优缺点,并通过实战案例分析了微服务架构在实际应用中的表现。微服务架构具有高内聚、低耦合、独立部署等优势,但也面临分布式系统的复杂性和较高的运维成本。通过某电商平台的实际案例,展示了微服务架构在提升系统性能和团队协作效率方面的显著效果,同时也指出了其带来的挑战。
85 4
|
3月前
|
运维 持续交付 API
深入理解并实践微服务架构:从理论到实战
深入理解并实践微服务架构:从理论到实战
155 3
|
3月前
|
自然语言处理 Java 网络架构
解锁跨平台微服务新纪元:Micronaut与Kotlin联袂打造的多语言兼容服务——代码、教程、实战一次打包奉送!
【9月更文挑战第6天】Micronaut是一款轻量级、高性能的Java框架,适用于微服务开发。它支持Java、Groovy和Kotlin等多种语言,提供灵活的多语言开发环境。本文通过创建一个简单的多语言兼容服务,展示如何使用Micronaut及其注解驱动特性实现REST接口,并引入国际化支持。无论是个人项目还是企业应用,Micronaut都能提供高效、一致的开发体验,成为跨平台开发的利器。通过简单的配置和代码编写,即可实现多语言支持,展现其强大的跨平台优势。
59 3
|
3月前
|
运维 监控 持续交付
深入浅出:微服务架构的设计与实战
微服务,一个在软件开发领域如雷贯耳的名词,它代表着一种现代软件架构的风格。本文将通过浅显易懂的语言,带领读者从零开始了解微服务的概念、设计原则及其在实际项目中的运用。我们将一起探讨如何将一个庞大的单体应用拆分为灵活、独立、可扩展的微服务,并分享一些实践中的经验和技巧。无论你是初学者还是有一定经验的开发者,这篇文章都将为你提供新的视角和深入的理解。
89 3
|
3月前
|
JSON 负载均衡 Java
微服务(二)-微服务实例
微服务(二)-微服务实例
|
1月前
|
设计模式 Java API
微服务架构演变与架构设计深度解析
【11月更文挑战第14天】在当今的IT行业中,微服务架构已经成为构建大型、复杂系统的重要范式。本文将从微服务架构的背景、业务场景、功能点、底层原理、实战、设计模式等多个方面进行深度解析,并结合京东电商的案例,探讨微服务架构在实际应用中的实施与效果。
129 6
|
1月前
|
设计模式 Java API
微服务架构演变与架构设计深度解析
【11月更文挑战第14天】在当今的IT行业中,微服务架构已经成为构建大型、复杂系统的重要范式。本文将从微服务架构的背景、业务场景、功能点、底层原理、实战、设计模式等多个方面进行深度解析,并结合京东电商的案例,探讨微服务架构在实际应用中的实施与效果。
51 1
|
20天前
|
Java 开发者 微服务
从单体到微服务:如何借助 Spring Cloud 实现架构转型
**Spring Cloud** 是一套基于 Spring 框架的**微服务架构解决方案**,它提供了一系列的工具和组件,帮助开发者快速构建分布式系统,尤其是微服务架构。
140 68
从单体到微服务:如何借助 Spring Cloud 实现架构转型
|
19天前
|
运维 监控 持续交付
微服务架构解析:跨越传统架构的技术革命
微服务架构(Microservices Architecture)是一种软件架构风格,它将一个大型的单体应用拆分为多个小而独立的服务,每个服务都可以独立开发、部署和扩展。
149 36
微服务架构解析:跨越传统架构的技术革命