在常规的开发中,每个微服务都包含代码和配置。其配置包含服务配置、各类开关和业务配置。如果系统结构中的微服务节点较少,那么常规的代码+配置的开发方式足以解决问题。当系统逐步迭代,其微服务会越来越复杂,慢慢演化成网状依赖结构,这个时候常规的代码+配置的开发方式就并不合适了,因为还要考虑整体系统的扩展性、伸缩性和耦合性等。这些问题中,配置的管理也是非常麻烦的。
spring cloud config 配置中心解决了微服务系统的配置中心化、配置版本控制、平台独立、语言独立等问题,其特性如下:
1.提供服务端和客户端支持(spring cloud config server 和 spring cloud config client)
2.集中式管理分布式环境中的配置信息
3.基于 spring 环境提供配置管理,与 spring 系列框架无缝结合
4.可用于任何语言开发环境
5.默认基于 GIT 仓库实现版本控制
<?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>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
</parent>
<groupId>com.csdn</groupId>
<artifactId>configserver</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
</project>
spring:
application:
name: config-server
cloud: # 配置 Spring Cloud Config 服务端相关信息
config:
server:
git:
uri: https://gitee.com/csdn_test/cloudconfig.git # 访问的远程仓库 uri
username: csdn_test # Gitee 用户名
password: enter_your_password # Gitee 密码
server:
port: 8888 # Spring Cloud Config 服务端默认端口为 8888,可以自定义修改。此端口为 Spring Cloud Config 客户端默认访问端口。
package com.csdn;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.config.server.EnableConfigServer;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
//@EnableConfigServer - 开启配置中心服务端。
//应用会根据全局配置文件访问GIT远程仓库,并将远程仓库中的配置内容下载到本地
@SpringBootApplication
@EnableConfigServer
@EnableEurekaClient
public class ConfigServerApp {
public static void main(String[] args) {
SpringApplication.run(ConfigServerApp.class, args);
}
}
<?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>
<artifactId>cloudhystrix</artifactId>
<groupId>com.csdn</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>applicationservice</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
</dependencies>
</project>
spring:
cloud:
config:
uri: http://localhost:8888/ # 分布式配置中心服务端访问地址,默认为 http://localhost:8888/
name: application-service-config # 要读取的配置文件主体命名
profile: default # 要读取的配置文件扩展环境名, 默认 default
label: master # 在 Gitee 仓库的哪一个分支中读取配置文件, 默认 null, 即在 master 分支中读取
1234567
<?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>
<artifactId>cloudhystrix</artifactId>
<groupId>com.csdn</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>applicationservice</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
</project>
spring:
cloud:
config:
uri: http://localhost:8888/ # 分布式配置中心服务端访问地址,默认为 http://localhost:8888/
name: application-service-config # 要读取的配置文件主体命名
profile: default # 要读取的配置文件扩展环境名, 默认 default
label: master # 在 Gitee 仓库的哪一个分支中读取配置文件, 默认 null, 即在 master 分支中读取
management:
endpoints:
web:
exposure:
include:
- refresh # 开启热刷新服务, 也可以在 Gitee 中的配置文件内定义。
- info
- health
package com.csdn.controller;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RefreshScope
public class ServiceController {
@Value("${test.str}")
private String str;
@PostMapping("/testPost")
public Object testPost(){
System.out.println("testPost method run");
return "写操作返回";
}
@GetMapping("/testGet")
public Object testGet(){
System.out.println("testGet method run");
return "读操作返回";
}
@GetMapping
public Object first(){
System.out.println("run");
try {
// 用于模拟远程服务调用延时
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "测试 Spring Cloud Netflix Ribbon 开发服务提供者";
}
}
远程的配置文件更新了,运维只需要发一个请求,所有用到这个配置文件的几百个应用更新了。
Spring Cloud Bus 是用轻量的消息中间件将分布式的节点连接起来,可以用于广播配置文件的更改或者服务的监控管理。关键的思想就是,消息总线可以为微服务做监控,也可以实现应用程序之间相通信。
Spring Cloud Bus 可选的消息中间件包括 RabbitMQ 和 Kafka 。
message queue
RabbitMQ 提供了 6 种工作模式:简单模式、work queues、Publish/Subscribe 发布与订阅模式、Routing
路由模式、Topics 主题模式、RPC 远程调用模式(远程调用,不太算 MQ;暂不作介绍)。
分别在 config-server 和 config-client中引入 bus依赖:bus-amqp
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-bus-amqpartifactId>
dependency>
分别在 config-server 和 config-client中配置 RabbitMQ
server 的配置文件
server:
port: 9527
spring:
application:
name: config-server
# spring cloud config
cloud:
config:
server:
# git 的 远程仓库地址
git:
uri: https://gitee.com/itheima_cch/itheima-configs.git
label: master # 分支配置
#配置rabbitmq信息
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest
virtual-host: /
# 将自己注册到eureka中
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka
# 暴露bus的刷新端点
management:
endpoints:
web:
exposure:
include: 'bus-refresh'
client的配置文件 bootstrap.yml
# 配置config-server地址
# 配置获得配置文件的名称等信息
spring:
cloud:
config:
# 配置config-server地址
# uri: http://localhost:9527
# 配置获得配置文件的名称等信息
name: config # 文件名
profile: dev # profile指定, config-dev.yml
label: master # 分支
# 从注册中心去寻找config-server地址
discovery:
enabled: true
service-id: CONFIG-SERVER
#配置rabbitmq信息
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest
virtual-host: /
management:
endpoints:
web:
exposure:
include: '*'
在config-server中设置暴露监控断点:bus-refresh
# 暴露bus的刷新端点
management:
endpoints:
web:
exposure:
include: 'bus-refresh'
启动测试
往配置中心发请求
curl -X POST http://localhost:9527/actuator/bus-refresh
也就是无需管mq的种类是什么,只需写业务逻辑即可,一套代码mq随便换,和jdbc原理差不多统一的一个管理。
rabbitTemplate.convertAndSend(“交换机”,luyoukey,“消息体”)
kafka.send()
1Spring Cloud Stream 是一个构建消息驱动微服务应用的框架。
2Stream 解决了开发人员无感知的使用消息中间件的问题,因为Stream对消息中间件的进一步封装,可以做到代码层面对中间件的无感知,甚至于动态的切换中间件,使得微服务开发的高度解耦,服务可以关注更多自己的业务流程。
3Spring Cloud Stream目前支持两种消息中间件RabbitMQ和Kafka
Stream 组件
Spring Cloud Stream 构建的应用程序与消息中间件之间是通过绑定器 Binder 相关联的。绑定器对于应用程序而言起到了隔离作用, 它使得不同消息中间件的实现细节对应用程序来说是透明的。
binding 是我们通过配置把应用和spring cloud stream 的 binder 绑定在一起
output:发送消息 Channel,内置 Source接口
input:接收消息 Channel,内置 Sink接口
pom
<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>
<artifactId>stream-parentartifactId>
<groupId>com.itheimagroupId>
<version>1.0-SNAPSHOTversion>
parent>
<modelVersion>4.0.0modelVersion>
<artifactId>stream-producerartifactId>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-stream-rabbitartifactId>
dependency>
dependencies>
project>
ProducerApp
package com.itheima.stream;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class ProducerApp {
public static void main(String[] args) {
SpringApplication.run(ProducerApp.class,args);
}
}
application.yml
server:
port: 8000
spring:
cloud:
stream:
# 定义绑定器,绑定到哪个消息中间件上
binders:
itheima_binder: # 自定义的绑定器名称
type: rabbit # 绑定器类型
environment: # 指定mq的环境
spring:
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest
virtual-host: /
bindings:
output: # channel名称
binder: itheima_binder #指定使用哪一个binder
destination: itheima_exchange # 消息目的地
消息发送类 MessageProducer
package com.itheima.stream.producer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.cloud.stream.messaging.Source;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Component;
@Component
@EnableBinding(Source.class)
public class MessageProducer {
@Autowired
private MessageChannel output;
public void send(){
String msessage = "hello stream~~~";
//发送消息
output.send(MessageBuilder.withPayload(msessage).build());
System.out.println("消息发送成功~~~");
}
}
调用的controller ProducerController
package com.itheima.stream.producer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ProducerController {
@Autowired
private MessageProducer producer;
@RequestMapping("/send")
public String sendMsg(){
producer.send();
return "success";
}
}
pom
<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>
<artifactId>stream-parentartifactId>
<groupId>com.itheimagroupId>
<version>1.0-SNAPSHOTversion>
parent>
<modelVersion>4.0.0modelVersion>
<artifactId>stream-consumerartifactId>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-stream-rabbitartifactId>
dependency>
dependencies>
project>
ConsumerApp
package com.itheima.stream;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class ConsumerApp {
public static void main(String[] args) {
SpringApplication.run(ConsumerApp.class,args);
}
}
application.yml
server:
port: 9000
spring:
cloud:
stream:
# 定义绑定器,绑定到哪个消息中间件上
binders:
itheima_binder: # 自定义的绑定器名称
type: rabbit # 绑定器类型
environment: # 指定mq的环境
spring:
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest
virtual-host: /
bindings:
input: # channel名称
binder: itheima_binder #指定使用哪一个binder
destination: itheima_exchange # 消息目的地
消息接收类 MessageListener
package com.itheima.stream.consumer;
import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.cloud.stream.annotation.StreamListener;
import org.springframework.cloud.stream.messaging.Sink;
import org.springframework.messaging.Message;
import org.springframework.stereotype.Component;
/**
* 消息接收类
*/
@EnableBinding({Sink.class})
@Component
public class MessageListener {
@StreamListener(Sink.INPUT)
public void receive(Message message){
System.out.println(message);
System.out.println(message.getPayload());
}
}