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

本文涉及的产品
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
简介: 概述 在上一篇中提到了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();
    }

}
目录
相关文章
|
1月前
|
运维 监控 Go
Go语言微服务实战与最佳实践
【2月更文挑战第14天】本文将深入探讨使用Go语言进行微服务实战中的最佳实践,包括服务拆分、API设计、并发处理、错误处理、服务治理与监控等方面。通过实际案例和详细步骤,我们将分享如何在Go语言环境中构建高效、稳定、可扩展的微服务系统。
|
21天前
|
Java fastjson 数据安全/隐私保护
【Dubbo3技术专题】「云原生微服务开发实战」 一同探索和分析研究RPC服务的底层原理和实现
【Dubbo3技术专题】「云原生微服务开发实战」 一同探索和分析研究RPC服务的底层原理和实现
38 0
|
1月前
|
开发框架 移动开发 JavaScript
SpringCloud微服务实战——搭建企业级开发框架(四十六):【移动开发】整合uni-app搭建移动端快速开发框架-环境搭建
正如优秀的软件设计一样,uni-app把一些移动端常用的功能做成了独立的服务或者插件,我们在使用的时候只需要选择使用即可。但是在使用这些服务或者插件时一定要区分其提供的各种服务和插件的使用场景,例如其提供的【uni-starter快速开发项目模版】几乎集成了移动端所需的所有基础功能,使用非常方便,但是其许可协议只允许对接其uniCloud的JS开发服务端,不允许对接自己的php、java等其他后台系统。
137 2
|
1月前
|
负载均衡 监控 算法
【微服务】1、一篇文章了解 Eureka 注册中心(理论加代码)
【微服务】1、一篇文章了解 Eureka 注册中心(理论加代码)
25 0
|
2月前
|
Java API 调度
从Spring Cloud 开始,聊一聊微服务架构的设计与实战
随着互联网的发展,网站应用的规模也在不断的扩大,进而导致系统架构也在不断的进行变化。
366 1
从Spring Cloud 开始,聊一聊微服务架构的设计与实战
|
2月前
|
存储 Dubbo 应用服务中间件
SpringCloud | Dubbo 微服务实战——注册中心详解
SpringCloud | Dubbo 微服务实战——注册中心详解
|
3月前
|
Prometheus 监控 Cloud Native
SpringCloud微服务实战——搭建企业级开发框架(四十五):【微服务监控告警实现方式二】使用Actuator(Micrometer)+Prometheus+Grafana实现完整的微服务监控
无论是使用SpringBootAdmin还是使用Prometheus+Grafana都离不开SpringBoot提供的核心组件Actuator。提到Actuator,又不得不提Micrometer,从SpringBoot2.x开始,Actuator的功能实现都是基于Micrometer的。
243 0
|
3月前
|
JSON Java 数据库
Spring Cloud【Finchley】实战-02订单微服务
Spring Cloud【Finchley】实战-02订单微服务
93 0
|
11天前
|
API 数据库 开发者
构建高效可靠的微服务架构:后端开发的新范式
【4月更文挑战第8天】 随着现代软件开发的复杂性日益增加,传统的单体应用架构面临着可扩展性、维护性和敏捷性的挑战。为了解决这些问题,微服务架构应运而生,并迅速成为后端开发领域的一股清流。本文将深入探讨微服务架构的设计原则、实施策略及其带来的优势与挑战,为后端开发者提供一种全新视角,以实现更加灵活、高效和稳定的系统构建。
18 0
|
10天前
|
Kubernetes 安全 Java
构建高效微服务架构:从理论到实践
【4月更文挑战第9天】 在当今快速迭代与竞争激烈的软件市场中,微服务架构以其灵活性、可扩展性及容错性,成为众多企业转型的首选。本文将深入探讨如何从零开始构建一个高效的微服务系统,覆盖从概念理解、设计原则、技术选型到部署维护的各个阶段。通过实际案例分析与最佳实践分享,旨在为后端工程师提供一套全面的微服务构建指南,帮助读者在面对复杂系统设计时能够做出明智的决策,并提升系统的可靠性与维护效率。