spring cloud 集成和使用
说明:
父工程文件如下:
spring boot 版本:2.6.8
spring cloud 版本:3.1.3
hystrix,停更以后没有最新版,所以用的:2.2.10.RELEASE
父工程 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>com.lomi.sc</groupId>
<artifactId>sc</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<!--子模块-->
<modules>
<module>eureka-server</module>
<module>user</module>
<module>order</module>
<module>goods</module>
<module>api</module>
<module>gateway</module>
<module>config</module>
</modules>
<dependencyManagement>
<dependencies>
<!--spring部分-->
<!--spring-boot-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.6.8</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
<version>2.6.8</version>
</dependency>
<!--服务注册和发现部分组件-->
<!--eureka-server-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
<version>3.1.3</version>
</dependency>
<!--eureka-client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
<version>3.1.3</version>
</dependency>
<!--cloud部分-->
<!--spring-cloud-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-context</artifactId>
<version>3.1.3</version>
</dependency>
<!--feign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>3.1.3</version>
</dependency>
<!--hystrix-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
<version>2.2.10.RELEASE</version>
</dependency>
<!--gateway-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
<version>3.1.3</version>
</dependency>
<!--config-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
<version>3.1.3</version>
</dependency>
<!--config 客户端需要的两个依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
<version>3.1.3</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
<version>3.1.3</version>
</dependency>
<!--bus-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
<version>3.1.2</version>
</dependency>
<!--sleuth和zipkin -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
<version>3.1.3</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-sleuth-zipkin</artifactId>
<version>3.1.3</version>
</dependency>
<!--spring cloud stream -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-stream-rabbit</artifactId>
<version>3.2.4</version>
</dependency>
<!--日志-->
<!--<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
<version>2.7.1</version>
</dependency>-->
</dependencies>
</dependencyManagement>
<build>
<plugins>
<!-- 指定项目jdk版本 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<!-- 打包 -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<!--可以把依赖的包都打包到生成的Jar包中 -->
<goal>repackage</goal>
</goals>
<configuration>
<attach>false</attach>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<!-- 跳过测试 -->
<properties>
<skipTests>true</skipTests>
</properties>
</project>
1 注册中心 Eureka 的集成和使用
分布式架构注册中心必不可少,dubbo 一般使用 的 zookeeper,spring cloud alibaba 使用 nacos
1 创建 项目 eureka-server
2 修改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>
<artifactId>sc</artifactId>
<groupId>com.lomi.sc</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>eureka-server</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-server</artifactId>
</dependency>
<!--gson 版本过低的问题也许需要这个-->
<!--<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.6.2</version>
</dependency>-->
</dependencies>
</project>
3 创建启动类
package com.lomi.eureka;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
import java.io.IOException;
/**
*
* 1 eureka 里面使用gson 版本太低,需要替换成版本或者使用 ,缺少 com.google.gson.GsonBuilder.setLenient 方法
* 2 可以用 @SpringBootApplication(exclude = {GsonAutoConfiguration.class}) 修饰,或者 引入高版本的 gson
*
*/
/**
*
*
* @author ZHANGYUKUN
*/
@EnableEurekaServer
@SpringBootApplication(exclude = {GsonAutoConfiguration.class})
public class Application {
public static void main(String[] args) throws IOException {
SpringApplication.run(Application.class, args);
System.out.println("*****************************");
System.out.println("********* eureka server 启动................... ***********");
System.out.println("*****************************");
System.in.read();
}
}
4 访问 http://127.0.0.1:9001/

2 spring cloud 集成 和 restemple 的使用
1 创建项目goods,order
2 pow.xml 文件,导入依赖,eureke 和 cloud(goods 和 order一样)
<!--eurake-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!--cloud-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-context</artifactId>
</dependency>
3 启动类(goods 和 order 样)
/**
*
*
* @author ZHANGYUKUN
*/
@SpringBootApplication
@EnableEurekaClient
public class Application {
public static void main(String[] args) throws IOException {
SpringApplication.run(Application.class, args);
System.out.println("*****************************");
System.out.println("********* goodsServer 启动................... ***********");
System.out.println("*****************************");
System.in.read();
}
}
4 goods 创建 restTemplate(写在任意spring boot 可以扫描到的地方),使用了 @LoadBalanced 注解以后,获使用负载均衡的方式调用消费者端,这时候 请求url 必须要是服务名,而不是能是ip
/**
* 创建 bean 的配置类
* @author ZHANGYUKUN
* @date 2022/6/24
*/
@Component
public class BeanCreatorConfig {
@Bean
@LoadBalanced
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
}
5 goods 调用 order controller
package com.lomi.goods.controller;
import com.lomi.api.order.OrderService;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.List;
/**
* @author ZHANGYUKUN
* @date 2022/6/24
*/
@RestController
@RequestMapping("shoppingCart")
@EnableDiscoveryClient
public class ShoppingCartController {
Logger log = LoggerFactory.getLogger(ShoppingCartController.class);
//引用配置文件里面写入的服务地址
@Value("${system.serverUrl.order}")
String orderServerUrl;
@Autowired
RestTemplate restTemplate;
/**
* 使用 restTemplate 调用 远程服务
*
* @return
*/
@PostMapping("generateOrder")
public String generateOrder() {
System.out.println("generateOrder" + ":" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
//查询购物车
List<String> goodsNames = Arrays.asList("商品1", "商品2", "商品2");
//调用order服务生成订单
String rt = restTemplate.postForObject(orderServerUrl +"orderServer/"+ "order/generate", goodsNames, String.class);
System.out.println(rt);
//清空购物车
return "OK";
}
}
6 goods application.yml 主要配置
## web ##
server:
port: 8001
servlet:
context-path: /goodsServer
system:
serverUrl:
order: "http://order-server/"
goods: "http://goods-server/"
feign:
httpclient: #配置http连接池
enabled: true
max-connection: 200
max-connections-per-route: 50
connection-timeout: 2000
7 order 端controller类似
package com.lomi.order.controller;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.web.bind.annotation.*;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
/**
* @author ZHANGYUKUN
* @date 2022/6/24
*/
@RestController
@RequestMapping("order")
public class OrderController {
Logger log = LoggerFactory.getLogger(OrderController.class);
@Value("${server.port}")
String port;
@PostMapping("generate")
public String generate(@RequestBody List<String> goodsInfo) throws InterruptedException {
System.out.println( "创建订单中:" + port +":"+ LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
//TimeUnit.SECONDS.sleep(5);
/* int i = 0;
if( i==0 ){
throw new RuntimeException("主动抛出异常");
}*/
System.out.println( "创建订单完成:" + port +":"+ LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
log.debug("创建订单完成");
return "orderGenerateOK";
}
public String generateFB(@RequestBody List<String> goodsInfo) throws InterruptedException {
return "服务器端FB";
}
}
3 feign 的集成和使用
feign 是 springcloud 对接口和远程 服务的封装,spring cloud 最基本的核心。现在用的一般都是 openfeign,基本使用的是 springcloud alibaba 也依旧会使用 feign
1 goods 和 order 引入依赖
<!--feign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
2 goods和order 启动类上添加 @EnableFeignClients启用 feign ,feign 默认使用负载均衡的方式调用消费者
/**
*
*
* @author ZHANGYUKUN
*/
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
public class Application {
public static void main(String[] args) throws IOException {
SpringApplication.run(Application.class, args);
System.out.println("*****************************");
System.out.println("********* goodsServer 启动................... ***********");
System.out.println("*****************************");
System.in.read();
}
3 goods 端或者创建一个工程用来定义 远程 api OrderService.java
package com.lomi.api.order;
import com.lomi.api.order.fallback.OrderServiceFallback;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import java.util.List;
/**
* @author ZHANGYUKUN
* @date 2022/6/24
*/
@FeignClient(value="order-server/orderServer")
public interface OrderService {
@PostMapping("order/generate")
String generate(@RequestBody List<String> goodsInfo);
}
4 goods 使用 fengn 接口调用远程服务
@Resource
OrderService orderService;
/**
* 使用 feign 调用 远程服务
*
* @return
*/
@PostMapping("generateOrderByFeign")
public String generateOrderByFeign() {
System.out.println("generateOrderByFeign" + ":" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
//查询购物车
List<String> goodsNames = Arrays.asList("商品1", "商品2", "商品2");
//调用order服务生成订单
String rt = orderService.generate(goodsNames);
System.out.println(rt);
log.debug("调用完成。。。。。。。。。。。。。。。");
//清空购物车
return rt;
}
远程的服务接口实现
@PostMapping("generate")
//@HystrixCommand(fallbackMethod = "generateFB")
public String generate(@RequestBody List<String> goodsInfo) throws InterruptedException {
System.out.println( "创建订单中:" + port +":"+ LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
//TimeUnit.SECONDS.sleep(5);
/* int i = 0;
if( i==0 ){
throw new RuntimeException("主动抛出异常");
}*/
System.out.println( "创建订单完成:" + port +":"+ LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
log.debug("创建订单完成");
return "orderGenerateOK";
}
public String generateFB(@RequestBody List<String> goodsInfo) throws InterruptedException {
return "服务器端FB";
}
4 hystrix 的集成和使用
服务降级和熔断是保护分布式集群可用的重要方式,hystrix 是spring cloud 早期核心的组件,但是现在缺乏维护,一般信项目部建议使用了,可以考虑 spring cloud alibaba 的 sentinel
1 order 和 goods pom 文件添加依赖
<!--hystrix-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
2 配置启动类上开启 hystrix
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
@EnableHystrix
public class Application {
..............
}
3 hystrix 可以使用 生产者或者消费者的 实现方法上(默认在这个方法抛出异常或者超时的时候返回,调用另外一个降级方法来返回一个默认值)
备注服务出现异常或者超时返回默认值, 这就是spring cloud 的服务降级,如果多次触发返回默认值,就会触发熔断机制,一定实践类直接返回默认值,不会调用实现方法,然后等这段时间窗口过期,获尝试放几个请求过来,如果请求成功者断路器关闭,否者当前时间窗口内继续熔断。熔断是服务降级的升级版。
/**
* 使用 feign 调用 远程服务
*
* @return
*/
@PostMapping("generateOrderByFeign")
@HystrixCommand(fallbackMethod = "generateOrderByFeignFB" )
public String generateOrderByFeign() {
System.out.println("generateOrderByFeign" + ":" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
//查询购物车
List<String> goodsNames = Arrays.asList("商品1", "商品2", "商品2");
//调用order服务生成订单
String rt = orderService.generate(goodsNames);
System.out.println(rt);
log.debug("调用完成。。。。。。。。。。。。。。。");
//清空购物车
return rt;
}
public String generateOrderByFeignFB() {
return "客户端FB";
}
4 也可以在 feign 接口上配置服务降级默认值
import java.util.List;
/**
* @author ZHANGYUKUN
* @date 2022/6/24
*/
@FeignClient(value="order-server",fallback = OrderServiceFallback.class)
public interface OrderService {
@PostMapping("order/generate")
String generate(@RequestBody List<String> goodsInfo);
}
/**
* @author ZHANGYUKUN
* @date 2022/6/27
*/
@Component
public class OrderServiceFallback implements OrderService {
@Override
public String generate(List<String> goodsInfo) {
return "OrderServiceFallback.generate.fallback";
}
}
5 由于hystrix 不怎么更新了,和springcloud 新版本有兼容问题,实测 超时时间和 feign 接口的服务降级默认方法 冲突,结果就是打开了 circuitbreaker=rtue(开启 feign 断路器) 以后,超时时间就无效了。
部分yml 文件配置
feign:
httpclient: #配置http连接池
enabled: true
max-connection: 200
max-connections-per-route: 50
connection-timeout: 2000
client: #配置超时间
config:
default:
connectTimeout: 4000
readTimeOut: 4000
# circuitbreaker: #设置了开了feign断路器以后,超时时间就不生效了不知道为什么(hystrix 已经停更了,版本比较老了,适配有问题)
# enabled: true
5 服务网关 gateway 的集成和使用
正常权限检查,请求过滤之类的东西分布式业务节点可以不处理,这些事情都可以交给 服务网关去做。并且 所有服务节点不会对外暴露,只有服务网关对外暴露,可以把 gateway 理解成一个 cloud 微服务专用的 服务器端代理兼路由器兼过滤器 ,之前是zuul效率比较低,现在的 gateway 使用netty 引入了非阻塞io,效率高了50%以上
1 创建项目gateway
2 引入pom.xml依赖文件
<!--gateway-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!--eureka-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!--gson-->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.6.2</version>
</dependency>
3 application.yml配置文件
## web ##
server:
port: 7001
#spring
spring:
application:
name: gateway-server
cloud:
gateway:
routes:
- id: shoppingCart_route #指定一个路由配置的拼命
uri: lb://goods-server/goods #路由地址,lb开头的是 负载均衡地址
predicates:
- Path=/goods/** # goods 开头的的地址会被转发到 上面的url 对应的地址
- id: order_route
uri: lb://order-server/orderServer
predicates:
- Path=/orderServer/**
filters: #为一个路由配置指定局部过滤器
- name: OrderFilter #这个局部过滤器使用实现类的名字死 OrderFilter+ GatewayFilterFactroy
args: #下面是指定的局部过滤器的构造参数
message: My Custom Message
preLogger: true
postLogger: true
discovery:
locator:
enabled: true #启用注册中心额定位器
#注册中心
eureka:
instance:
hostname: localhost
prefer-ip-address: true
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://localhost:9001/eureka/
4 启动类
@SpringBootApplication
@EnableEurekaClient
public class Application {
public static void main(String[] args) throws IOException {
SpringApplication.run(Application.class, args);
System.out.println("*****************************");
System.out.println("********* gateway 启动................... ***********");
System.out.println("*****************************");
System.in.read();
}
}
5 上面配置指定的局部过滤器(id=order_route的过滤器指定的这个过滤器),需要实现AbstractGatewayFilterFactory接口
/**
*
* filters:名字是这个类名的前缀 OrderFilter_GatewayFilterFactory
*
* @Author ZHANGYUKUN
* @Date 2022/6/28
**/
@Component
public class OrderFilterGatewayFilterFactory extends AbstractGatewayFilterFactory<OrderFilterGatewayFilterFactory.Config> {
public OrderFilterGatewayFilterFactory() {
super(Config.class);
}
@Override
public GatewayFilter apply(Config config) {
return new GatewayFilter() {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
System.out.println( "orderServer的局部过滤器..............." );
return chain.filter(exchange).then(Mono.fromRunnable(() -> {
if (config.isPostLogger()) {
System.out.println( "orderServer的局部过滤器 回链做点事..............." );
}
}));
}
};
}
public static class Config {
private String message;
private boolean preLogger;
private boolean postLogger;
public Config() {
}
public Config(String message, boolean preLogger, boolean postLogger) {
this.message = message;
this.preLogger = preLogger;
this.postLogger = postLogger;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public boolean isPreLogger() {
return preLogger;
}
public void setPreLogger(boolean preLogger) {
this.preLogger = preLogger;
}
public boolean isPostLogger() {
return postLogger;
}
public void setPostLogger(boolean postLogger) {
this.postLogger = postLogger;
}
}
}
6 配置一个全局过滤器,对所有路由的请求都有效(需要实现GlobalFilter接口)
@Component
public class TokenFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
System.out.println("----token 过滤器 ----");
System.out.println("请求路径是:" + exchange.getRequest().getPath() );
//有些路径不需要过滤,可以在这里处理
//检查token
String token = exchange.getRequest().getHeaders().getFirst("token");
//如果没有token直接返回失败
if ( token == null || token.trim().length() == 0 ){
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
ServerHttpResponse response = exchange.getResponse();
return response.setComplete();
}
//检查token是否合法
System.out.println("请求的token是:" + token );
if( token.equals("123") ){
exchange.getResponse().setStatusCode(HttpStatus.OK);
ServerHttpResponse response = exchange.getResponse();
byte[] bits = "全局过滤器直接返回的数据".getBytes(StandardCharsets.UTF_8);
DataBuffer buffer = response.bufferFactory().wrap(bits);
return response.writeWith(Mono.just(buffer));
}
return chain.filter(exchange).then(Mono.fromRunnable(
() -> System.out.println("回链响应也可以做点事..")
));
}
@Override
public int getOrder() {
return 1;
}
}
6 配置中心spring config 的集成和使用
spring config 作为分布式配置中心,集中的管理分布式配置文件(类似 dubbo 框架使用 百度 disconf 最为配置管理中心 )
注意关于启动配置类的配置需要配置到bootstrap.xml里面去
1 创建 config 服务工程
2 config 工程引入 poxm 文件
<!--actuator-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!--config-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.6.2</version>
</dependency>
<!--eurake cilent-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
3 config工程 yml,下面
cloud.config下面是 git 的地址 账号密码和分支
## web ##
server:
port: 6001
#spring
spring:
application:
name: config-server
cloud:
config:
server:
git:
uri: https://gitee.com/octupus/config.git #git地址
username: XXXX
password: XXX
default-label: master #git分支
#注册中心
eureka:
instance:
hostname: localhost
prefer-ip-address: true
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://localhost:9001/eureka/
4 config 项目启动类
@SpringBootApplication
@EnableEurekaClient
@EnableConfigServer
public class Application {
public static void main(String[] args) throws IOException {
SpringApplication.run(Application.class, args);
System.out.println("*****************************");
System.out.println("********* configServer 启动................... ***********");
System.out.println("*****************************");
System.in.read();
}
}
5 goods 引用 config 项目的配置 pom文件引入依赖
<!--config-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.6.2</version>
</dependency>
6 创建 bootstrap.yml 配置文件,和application.yml同位置,但是bootstrap.yml 加载顺序比 application.yml高,把关于 配置中心的配置都移动过去
spring:
profiles:
active: dev
main:
allow-circular-references: true
application:
name: goods-server
cloud:
config:
discovery:
enabled: true
service-id: config-server
#uri: http://localhost:6001/configServer #配置中心地址
label: master #git分支
profile: dev #项目使用profiles
name: goodsServer #配置文件的名字
7 goods 获取配置文件(@RefreshScope,标注的类,里面的配置文件可以在在指定情况下事实刷新)
/**
* 获取配置文件
*/
@RestController
@RequestMapping("config")
@RefreshScope
public class ConfigController {
//引用config的 远程文件地址
@Value("${abc:000}")
String version;
/**
* 获取配置文件
* @return
*/
@PostMapping("showConfig")
public String showConfig() {
System.out.println( "我取到的配置文件是:" + version );
return version;
}
}
8 默认情况下,goods 只会在启动的时候获取一个 config 的配置,如果要需要手动刷新,可以开放 good是刷新暴露点,然后手动刷新(需要和@RefreshScope配合使用)
加在 application.yml或者 bootstrap.yml都可以
#暴露刷新点
management:
endpoints:
web:
exposure:
include: "refresh"
调用刷新地址:post http://服务地址/程序访问前缀/actuator/refresh(这样适合当个引用的刷新)
7 消息总线spring bus 的使用
作用就一个,动态的推送分布式配置到各个分布式节点,而不是各个节点重启来拉取
注意刷新点改名字了
如果需要全局配置刷新需要配置 spring bus
1 config 导入 bus 的依赖
<!--bus--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-bus-amqp</artifactId> </dependency>
2 config 项目 application.yml 导入 mq 的配置
#spring
spring:
application:
name: config-server
cloud:
config:
server:
git:
uri: https://gitee.com/octupus/config.git
username: 451740146@qq.com
password: a5464459480
default-label: master
rabbitmq:
host: 127.0.0.1
port: 5672
username: guest
password: guest
3 config项目 application.yml配置 暴露刷新点
#暴露刷新点
management:
endpoints:
web:
exposure:
include: 'bus-refresh' #新版本改成 actuator/busrefresh post请求,别得用法都一样,刷新指定节点actuator/busrefresh/服务名字:端口
4 goods 在 pom 中 导入消息中线依赖
<!--bus 消息总线-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
5 goods 在 yml 文件中配置 mq 地址(此配置 和 cloud属性配置缩进的同级别 )
#spring
spring:
profiles:
active: dev
main:
allow-circular-references: true
application:
name: goods-server
cloud:
config:
discovery:
enabled: true
service-id: config-server
#uri: http://localhost:6001/configServer
label: master
profile: dev
name: goodsServer
rabbitmq:
host: 127.0.0.1
port: 5672
username: guest
password: guest
6 调用config刷新点就能刷新指定项目的配置
调用config刷新地址:post http://服务地址/程序访问前缀/actuator/busrefresh (可以刷新所有使用config 项目配置的其它项目,原理就是通过 mq 发送一个 所有子项目订阅的广播消息 )
调用config刷新地址:post http://服务地址/程序访问前缀/actuator/busrefresh/服务名字:端口 可以指定刷新的子项目
8 spring stream 消息中间件的 代理
spring cloud stream ,可把它当做是所有消息中间键的抽象接口(l类似 slf4j 和各种 日志实现的关系),使用它一定程度上不用在关心 mq的具体api(个人觉得没啥用,初级接口可以通用,特性接口依旧需要学习各种mq 的特性)
下面例子是 stream-rabbit 3.1 以后的用法,3.1 之前的用法的已经过期,不建议用了
在 goods 中使用 cloud stream 接受 rabbit mq 的消息
1 导入依赖
<!--stream-rabbit-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-stream-rabbit</artifactId>
</dependency>
2 配置文件 修改
spring:
profiles:
active: dev
main:
allow-circular-references: true
application:
name: goods-server
cloud:
config:
discovery:
enabled: true
service-id: config-server
#uri: http://localhost:6001/configServer
label: master
profile: dev
name: goodsServer
stream: #stream 和上面的 cloud 属性同级别
function:
definition: myChannel #这里定义那些Spring 托管的bean 是消息监听者(这个非常重要,必不可少,不然spring不知道哪些bean 是消费监听者)
binders: # 一个 bingder 就是一个mq地址
myRabbitMQ: #这个名字自己取得,指定mq 的名字
type: rabbit #指定mq 的类型
environment:
spring: #下面是mq 的一下具体参数
rabbitmq:
addresses: localhost:5672
username: guest
password: guest
virtual-host: /
bindings:
myChannel-out-0: #指定一个输出渠道的名字,格式:渠道名字-out-N
destination: myExchange #目的地,在rabbitmq 中赌赢 交换机的名字
myChannel-in-0: #输出渠道的名字
destination: myExchange #交换机的名字,需要额输入渠道交换机名字一样
group: myTestQueue #交换机绑定的队列名字
binder: myRabbitMQ #使用的那个binder(binders里面的指定的名字)
consumer:
concurrency: 1 #并行数
rabbit:
bindings:
myChannel-in-0: #如果要指定一些特定参数,比如手动确认,这里给指定 输入渠道 配置参数
consumer:
acknowledgeMode: manual #设置位手动确认
3 消息生产者
streamBridge.send 的第一个参数是 输出渠道的全名字,第二个参数是消息,可以是直接是我们的消息对象,也可以是org.springframework.messaging.Message<T> 里面包裹着我们的消息对象
@Autowired
StreamBridge streamBridge;
//发送消息
public String sendMessage(Object message, Map<String, Object> properties) {
streamBridge.send("myChannel-out-0", "这是消息体");
System.out.println("发送消息成功");
return null;
}
4 消息消费者 Consumer<Message<String>> , 这里的 Consumer<T> 里面的 T可以是我们消息对象的类型,也可以是org.springframework.messaging.Message<T> 里面装着我么你的消息
备注:默认是自动确认,手动确认需要配置文件里面 acknowledgeMode: manual 配合
/**
* 消息接受者,并且手动确认 方法名字 myChannel1 是 myChannel1-in-0 的前缀,在cloud.stream.function 里面申明过。
* @return
*/
@Bean
public Consumer<Message<String>> myChannel() {
return message -> {
System.out.println("******************");
System.out.println("At Sink1");
System.out.println("******************");
System.out.println("Received message " + message.getPayload());
Channel channel = message.getHeaders().get(AmqpHeaders.CHANNEL, Channel.class);
Long deliveryTag = message.getHeaders().get(AmqpHeaders.DELIVERY_TAG, Long.class);
try {
channel.basicAck(deliveryTag, false);
} catch (IOException e) {
e.printStackTrace();
}
};
}
9 分布式链路追踪 zipkin 集成和使用
提供一个可视化的,可控采样率的分布式调用链路追踪工具,效率影响比较大,不建议生产使用,基本是用mq 代替http请求
1 引入配置,所有需要链路最终的都需要
<!--sleuth 和 zipkin client -->
<!-- zipkin 默认使用的 http连接,可以改成 mq方式,存储zipkin 信息可以选择 es 或者 msql -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-sleuth-zipkin</artifactId>
</dependency>
2 修改配置yml
zipkin:
base-url: http://127.0.0.1:9411 #zipkin的地址,如果真要使用建议 下载 zipkin 源码修改 配置中心地址,然后加入到集群环境在使用,而不是直接使用绝对地址
sleuth:
sampler:
probability: 1 #采样率0-1,默认使用http 的方式向 zipkin 记录数据,很影响效率,如果一定要用建议使用mq 的方式,采样率越高影响的效率越高
3 引入以后 默认会修改日志文件 ,把 traceId 和 spanId加入到日志中
traceId:一次跨多次节点调用的唯一ID
spanId:每个节点在本次分布式过程中的id
4 如果需要手动指定 上面两个分布式追踪ID的 位置
手动打印格式为:[%X{traceId},%X{spanId}]
5 所有调追踪记录都会出现在 zipkin 服务中。默认是不存盘的,需要存盘可以选择mysql es 之类的存储工具
浏览器访问:http://127.0.0.1:9411 可以查看最终信息。或者日志里面的最终ID 也可以参考
相关例子代理记录于:https://gitee.com/octupus/sc
能耍的时候就一定要耍,不能耍的时候一定要学。
--天道酬勤,贵在坚持posted on 2022-06-29 22:30 zhangyukun 阅读(315) 评论(0) 收藏 举报
浙公网安备 33010602011771号