文章目录
- 前言
- 一、引出
- 二、服务注册与发现
- 2.1 创建Eureka注册中心
- 2.1.1 引入pom依赖
- 2.1.2 配置yaml
- 2.1.3 启动服务
- 21.4 测试访问
- 2.2 创建服务提供者
- 2.2.1 配置yaml
- 2.2.2 启动服务
- 2.2.3 测试访问
- 2.3 创建服务消费者
- 2.3.1 服务提供者接口
- 2.3.2 服务消费者调用接口
- 三、负载均衡
- 3.1 Ribbon概念
- 3.2 负载均衡
- 3.3 修改负载均衡算法
- 四、服务调用
- 4.1 概念
- 4.2 创建服务调用公共模块
- 4.3 消费者服务开启服务调用
- 五、服务降级熔断
- 5.1 修改服务调用API公共模块
- 5.2 消费者修改配置文件
- 六、网关
- 6.1 导入pom依赖
- 6.2 修改yaml配置文件
- 6.3 开启网关代理
- 6.4 测试服务
- 6.5 路由访问规则映射
- 七、配置中心
- 7.2 配置configservice服务
- 7.1 配置adminservice服务
- 7.3 配置portal服务
- 7.4 SpringCloud整合apollo
- 7.4.1 引入pom依赖
- 7.4.2 启动服务配置
- 7.4.3 修改yaml配置
- 7.4.4 新增apollo配置
- 7.4.5 新增接口测试
- 总结
前言
随着技术的不断更新,微服务的组件也一直在换代推出,本篇文章我们就来学习一下微服务组件的落地实现,其实任何组件使用方式都是一样的。
一、引出
本篇文章主要是微服务项目实战的构建,以及微服务组件之间的交互,其实小编之前就有写过SpringCloud + SpringCloud Alibaba那一套架构了,也是互联网比较新的技术栈,用Nacos做注册中心、配置中心、Gateway做网关等等,本篇相当于是回顾最开始微服务的架构组件,可能很多小伙伴仅限于使用当前最新的技术栈,比如Alibaba的Nacos这一套微服务,换做是Eureka这一套可能会觉得不一样,其实架构体系都是一样的,只是Cloud一直在做升级,比如之前的Eureka、Zuul、Histrix已经停更了,官方推荐使用的是Nacos、Gateway、Sentienl这一套。
本篇文章的springcloud、boot版本如下:
<spring.boot.version>2.1.13.RELEASE</spring.boot.version>
<spring.cloud.version>Greenwich.SR6</spring.cloud.version>
<spring.cloud.alibaba.version>2.1.4.RELEASE</spring.cloud.alibaba.version>
二、服务注册与发现
这里我们使用的是Eureka来做服务注册于发现,还有像Nacos也可以做,本篇文章重点在回顾旧版本的一个微服务链路。
父工程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">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>springcloud-pro</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<modules>
<module>spring-cloud-provider</module>
<module>spring-cloud-consumer</module>
<module>spring-cloud-eureka</module>
<module>spring-cloud-provider-two</module>
<module>spring-cloud-api</module>
<module>spring-cloud-zuul</module>
</modules>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.13.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<!--核心版本控制-->
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<java.version>1.8</java.version>
<maven.plugin.version>3.8.1</maven.plugin.version>
<spring.boot.version>2.1.13.RELEASE</spring.boot.version>
<spring.cloud.version>Greenwich.SR6</spring.cloud.version>
<spring.cloud.alibaba.version>2.1.4.RELEASE</spring.cloud.alibaba.version>
<lombok.version>1.18.22</lombok.version>
<fastjson.version>1.2.83</fastjson.version>
<mysql.version>8.0.19</mysql.version>
<druid.version>1.2.4</druid.version>
<mybatis.plus.version>3.4.3.4</mybatis.plus.version>
<canal.version>1.1.4</canal.version>
<okhttp.version>11.7</okhttp.version>
</properties>
<!--引入公共依赖全局使用-->
<dependencies>
<!--Lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</dependency>
<!--fastjson-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fastjson.version}</version>
</dependency>
<!--测试依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<!--声明依赖-->
<dependencyManagement>
<dependencies>
<!-- Spring Boot 配置 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring.boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- Spring Cloud 微服务 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring.cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- Spring Cloud Alibaba 微服务 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring.cloud.alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
</resources>
<!--打包插件声明,子类中不用指定版本号-->
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<encoding>${project.build.sourceEncoding}</encoding>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
2.1 创建Eureka注册中心
2.1.1 引入pom依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
2.1.2 配置yaml
server:
port: 8081
eureka:
instance:
hostname: localhost #eureka服务端的实例名称
client:
fetch-registry: false #false表示自己断就是注册中心
register-with-eureka: false #false表示不向注册中心注册自己
service-url:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
2.1.3 启动服务
注入@EnableEurekaServer,自动加载eureka配置。
@EnableEurekaServer
@SpringBootApplication
public class ProviderApplication {
public static void main(String[] args) {
SpringApplication.run(ProviderApplication.class, args);
}
}
21.4 测试访问
浏览器输入地址:http://localhost:8081/,如果页面如下,则配置eureka成功
2.2 创建服务提供者
2.2.1 配置yaml
server:
port: 8082
spring:
application:
name: spring-cloud-provider # 微服务名称
eureka:
client: # 客户端注册进eureka列表,集群使用逗号分隔
service-url:
defaultZone: http://localhost:8081/eureka/
instance:
instance-id: spring-cloud-provider-8082 # 别名
prefer-ip-address: true # 访问路径可以显示IP地址
2.2.2 启动服务
使用@EnableEurekaClient ,服务启动后自动注册到eureka服务端
/**
* @author: DT辰白 Created by 2024/5/3 18:35
*/
@EnableEurekaClient // 服务启动后自动注册到eureka服务端
@SpringBootApplication
public class ProviderApplication {
public static void main(String[] args) {
SpringApplication.run(ProviderApplication.class, args);
}
}
2.2.3 测试访问
再次访问eureka注册中心,可以看到服务提供者已经注册进去了。
2.3 创建服务消费者
其余步骤和服务提供者一样,我们只需要发起一个Http请求测试服务调用即可。
2.3.1 服务提供者接口
@RestController
@RequestMapping("/provider")
public class ProviderController {
@GetMapping("/get")
public String get() {
return "Hello!";
}
}
2.3.2 服务消费者调用接口
@RestController
@RequestMapping("/consumer")
public class ConsumerController {
@Autowired
private RestTemplate restTemplate;
@GetMapping("/get")
public String get() {
ResponseEntity<String> responseEntity = restTemplate.getForEntity("http://localhost:8082/provider/get", String.class);
return responseEntity.getBody();
}
}
三、负载均衡
微服务中,服务之间的调用离不开负载均衡,保证高可用、高性能的支持,所以引入了负载均衡组件,业界使用最广的就是以Ribbon为代表的负载均衡组件。
3.1 Ribbon概念
Ribbon 是一个基于 HTTP 和 TCP 的 客服端负载均衡工具,它是基于 Netflix Ribbon 实现的。
它不像 Spring Cloud 服务注册中心、配置中心、API 网关那样独立部署,但是它几乎存在于每个 Spring Cloud 微服务中。包括 Feign 提供的声明式服务调用也是基于该 Ribbon 实现的。
Ribbon 默认提供很多种负载均衡算法,例如轮询、随机等等,甚至包含自定义的负载均衡算法。由于我们使用了eureka的pom依赖,已经包含了Ribbon依赖了,所以在使用的时候不需要单独进行引入。
3.2 负载均衡
Ribbon的功能就是做负载均衡的,由于我们使用的是RestTemplate来进行HTTP服务调用,所以只需要设置RestTemplate支持负载均衡即可,修改如下配置文件:
修改远程调用接口,改为通过服务名称调用:
测试之后,依然能够成功访问到,并且启动两个生产者实例,修改端口号即可,保持微服务名称相同,启动服务测试,也能做到负载均衡的效果,默认是轮询的方式。
此时查看eureka注册中心,是否都注测进来了。
3.3 修改负载均衡算法
总共有7种负载均衡算法:
显示声明,选择使用哪种算法。
四、服务调用
有了服务注册、服务发现之后,我们的微服务之间就要进行服务调用了,下面我们使用OpenFeign来实操一遍。
4.1 概念
上面我们采用的是 【RestTemplate + Ribbon】 的策略来实现服务调用,另外介绍一个HTTP客户端调用Feign,Feign是Spring Cloud中的一个声明式的HTTP客户端库,用于简化编写基于HTTP的服务调用代码,在编码中只需要一创建一个接口,然后在上面添加注解即可。相比于上面的调用方式,Feign相当于是在此基础上做了封装,简化了开发量。
注意:但是从Spring Cloud 2020版本开始,官方宣布Feign将不再维护和支持,推荐使用OpenFeign作为替代方案,但是,随着SpringCloud 2022的发布,官方宣布OpenFeign将被视为功能完整。这意味着Spring Cloud团队将不再向模块添加新特性。只会修复bug和安全问题。其实,之所以OpenFeign后期不再更新,主要是因为在Spring 6.0 发布之后,Spring内置了一个HTTP客户端 @HttpExchange ,而官方肯定建议大家使用这个自带客户端进行HTTP调用。
4.2 创建服务调用公共模块
一般在服务调用模块,我们都会单独创建一个公共API调用模块,以供使用,新建spring-cloud-api 模块,导入pom依赖。
新建一个接口,添加注解:
/**
* @author: DT辰白 Created by 2024/5/4 15:47
*/
@Service
@FeignClient(value = "spring-cloud-provider")
public interface RpcProviderFeignService {
@GetMapping("/provider/get")
String get();
}
4.3 消费者服务开启服务调用
在消费者服务,导入spring-cloud-api模块,在启动类上开启OpenFeign服务调用。
<dependency>
<groupId>org.example</groupId>
<artifactId>spring-cloud-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
@Autowired
private RpcProviderFeignService rpcProviderFeignService;
@GetMapping("/getOpenFeign")
public String getOpenFeign() {
String s = rpcProviderFeignService.get();
log.info("openfeign调用成功,返回结果【{}】", s);
return s;
}
五、服务降级熔断
有了服务注册、服务发现、服务调用、负载均衡之后,我们的微服务之间调用出现异常或者超时,我们需要对服务进行降级熔断,下面我们使用hystrix来实操一遍。
5.1 修改服务调用API公共模块
fallback兜底方案:
自动配置类加载:
5.2 消费者修改配置文件
feign: # feign开启hystrix
hystrix:
enabled: true
以上即可完成服务降级熔断的处理了。
六、网关
Zuul网关提供代理+路由+过滤三大功能,除此之外还有Gateway也可以做网关,本篇我们讨论Zuul网关,创建一个网关服务。
6.1 导入pom依赖
在pom文件中新增 Zuul 配置:
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
</dependencies>
6.2 修改yaml配置文件
server:
port: 8086
spring:
application:
name: spring-cloud-zuul
eureka:
client: # 客户端注册进eureka列表,集群使用逗号分隔
service-url:
defaultZone: http://localhost:8081/eureka/
instance:
instance-id: spring-cloud-zuul-8086 # 别名
prefer-ip-address: true # 访问路径可以显示IP地址
6.3 开启网关代理
启动类加上开启 Zuul 的注解:
@EnableZuulProxy // 开启网关
@SpringBootApplication
public class ZuulApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulApplication.class);
}
}
6.4 测试服务
访问消费服务:http://localhost:8086/spring-cloud-consumer/consumer/getOpenFeign,其中spring-cloud-consumer为消费者的服务名称。
6.5 路由访问规则映射
在之前:http://localhost:8086/spring-cloud-consumer/consumer/getOpenFeign
如果我们觉得服务名:【spring-cloud-consumer】太长,或者是不想暴露服务名,想用简洁的字段来替代,可以用如下配置:
server:
port: 8086
spring:
application:
name: spring-cloud-zuul
eureka:
client: # 客户端注册进eureka列表,集群使用逗号分隔
service-url:
defaultZone: http://localhost:8081/eureka/
instance:
instance-id: spring-cloud-zuul-8086 # 别名
prefer-ip-address: true # 访问路径可以显示IP地址
zuul: # 网关路由规则配置
#所有服务路径前统一加上前缀
prefix: /api
ignored-services: "*"
host:
maxTotalConnections: 200 #连接池最大连接数,仅用于Apache的HttpClient,对于okhttp和restclient无效
maxPerRouteConnections: 20 #每个路由最大连接数,仅用于Apache的HttpClient,对于okhttp和restclient无效
routes:
spring-cloud-consumer:
path: /cm/**
serviceId: spring-cloud-consumer
spring-cloud-provider:
path: /pv/**
serviceId: spring-cloud-provider
之后访问就变成这样:http://localhost:8086/cm/consumer/getOpenFeign
Zuul默认使用 Apache 的 HttpClient 作为HTTP客户端发送请求,超时参数和连接池参数配置如下:
zuul:
host:
maxTotalConnections: 200 #连接池最大连接数,仅用于Apache的HttpClient,对于okhttp和restclient无效
maxPerRouteConnections: 20 #每个路由最大连接数,仅用于Apache的HttpClient,对于okhttp和restclient无效
七、配置中心
下载源码:https://github.com/apolloconfig/apollo/releases
找到scripts目录下的build.bat脚本文件,直接双击进行maven打包。
将【configservice、adminservice、portal】三个服务ZIP包传到Linux中的/opt/soft目录下。
依次解压,按照【configservice、adminservice、portal】顺序依次启动服务。
7.2 配置configservice服务
创建【apolloconfigdb】数据库,导入SQL脚本,修改配置表信息:
修改eureka注册地址:
修改数据库连接地址:
[root@localhost apolloconfig]# cd config/
[root@localhost config]# vi application-github.properties
启动服务:
[root@localhost apolloconfig]# cd scripts/
[root@localhost scripts]# ./startup.sh
查看eureka注册中心是否已经将configservice注册进来了:
http://192.168.78.190:8080/
7.1 配置adminservice服务
修改数据库连接地址:
[root@localhost apolloadmin]# cd config/
[root@localhost config]# vi application-github.properties
启动服务:
[root@localhost apolloadmin]# cd scripts/
[root@localhost scripts]# ./startup.sh
查看eureka注册中心是否已经将configservice注册进来了:
http://192.168.78.190:8080/
7.3 配置portal服务
创建【apolloportaldb】数据库,导入SQL脚本,修改配置表信息:
修改为【dev】环境:
修改数据库连接地址:
// 修改数据库连接地址
[root@localhost apolloportal]# cd config/
[root@localhost config]# vi application-github.properties
修改不同环境的metaServer:
[root@localhost apolloportal]# cd config/
[root@localhost config]# vi apollo-env.properties
这里其实就是修改eureka的地址:
启动服务:
[root@localhost apolloportal]# cd scripts/
[root@localhost scripts]# ./startup.sh
登录控制台:http://192.168.78.190:8070 默认端口为8070,默认账号密码为 apollo/admin
7.4 SpringCloud整合apollo
7.4.1 引入pom依赖
<dependency>
<groupId>com.ctrip.framework.apollo</groupId>
<artifactId>apollo-client</artifactId>
<version>1.6.0</version>
</dependency>
7.4.2 启动服务配置
@EnableApolloConfig // 开启apollo服务配置
@EnableFeignClients(basePackages ={"org.example.api.feign"}) // 开启服务调用
@EnableEurekaClient // 服务启动后自动注册到eureka服务端
@EnableDiscoveryClient // 服务发现
@SpringBootApplication
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class, args);
}
}
7.4.3 修改yaml配置
#apollo配置
app:
id: spring-cloud-consumer # 与apollo的admin管理界面添加的 appId 一致
apollo:
meta: http://192.168.78.190:8080 #填写configservice项目的地址
bootstrap:
enabled: true #在应用启动阶段,向Spring容器注入被托管的application.properties文件的配置信息。
namespaces: application,TEST1.redis,kafka # 从 namespace 中获取配置, 多个以逗号隔开, namespace的配置非properties格式的需要加后缀名
eagerLoad:
enabled: true #将Apollo配置加载提到初始化日志系统之前。
7.4.4 新增apollo配置
点击新增配置、然后点击发布即可完成配置,默认1s即可刷新配置,并且不用重启SpringBoot服务。
也可以创建一个灰度发布任务:
灰度发布是指在黑与白之间,能够平滑过渡的一种发布方式。AB test就是一种灰度发布方式,让一部分用户继续用A,一部分用户开始用B,如果用户对B没有什么反对意见,那么逐步扩大范围,把所有用户都迁移到B上面来。灰度发布可以保证整体系统的稳定,在初始灰度的时候就可以发现、调整问题。
全量发布的效果是:
1)灰度版本的配置会合并回主版本
2)主版本的配置会自动进行一次发布
3)在全量发布页面,可以选择是否保留当前灰度版本,默认为不保留。
7.4.5 新增接口测试
@Value("${apollo.test001}")
private String apolloTest;
@Value("${redis.username}")
private String redisUsername;
@Value("${kafka.timeout}")
private String timeout;
@Value("${my.username}")
private String myusername;
@GetMapping("/getConfig")
public String getConfig() {
log.info("test001={}",apolloTest);
log.info("redisUsername={}",redisUsername);
log.info("timeout={}",timeout);
log.info("myusername={}",myusername);
return apolloTest+","+redisUsername + "," + timeout + "," + myusername;
}
总结
针对这个架构体系的SpringCloud微服务,我们就到这里结束了,其中我们引入了apollo配置中心,大家可以对比Ncoas以及Config,它们之间的优缺点,关于apollo的核心概念篇,后续会单独开篇讲解,这篇文章旨在为大家回顾老版本的微服务架构体系,任何新老技术,其实都是在一步步升级性能以及用户体验,所以我们多去实操,就能够将整个知识体系完整运用到项目中,哪怕遇到新老技术交替使用,也不至于无从下手。