第六课 Spring Cloud分布式微服务实战-微服务Spring Cloud
tags:
- Java
- 慕课网
文章目录
第一节 SpringCloud介绍
1.1 SpringCloud 概述与版本选型
- springcloud官网介绍:https://spring.io/microservices
- springcloud相关版本:https://spring.io/projects/spring-cloud
- 这里因为使用Boot 2.2.5所以spring cloud使用Hoxton。
1.2 构建eureka注册服务
- 新建pringcloud-eureka模块,配置启动类。引用api模块。
package com.imooc.eureka;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.ComponentScan;
import tk.mybatis.spring.annotation.MapperScan;
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class,
MongoAutoConfiguration.class})
@EnableEurekaServer // 开启注册中心
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
<dependencies>
<dependency>
<groupId>com.imooc</groupId>
<artifactId>imooc-news-dev-service-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies>
- application.yml配置文件。测试一下。http://localhost:7000/
############################################################
#
# eureka 注册中心
# web访问端口号 约定:7000
#
############################################################
server:
port: 7000
tomcat:
uri-encoding: UTF-8
############################################################
#
# 配置项目信息
#
############################################################
spring:
application:
name: springcloud-eureka
############################################################
#
# eureka 配置信息
#
############################################################
eureka:
instance:
# eureka 实例的hostname,可以是hostname,也可以自定义配置hostname
hostname: eureka
client:
# 是否要把当前的eureka server注册到自己
register-with-eureka: false
# 从注册中心获得检索服务实例,server没有必要,直接false即可
fetch-registry: false
# 单实例配置自己的服务地址,高可用集群则配置多个地址
service-url:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
1.3 其他服务注册到eureka上
- api模块中引入依赖。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
- 对user服务中application.yml配置eureka相关信息。
eureka:
# 自定义eureka server的信息
server:
hostname: eureka
port: 7000
client:
# 所有的微服务都必须注册到eureka中
register-with-eureka: true
# 从注册中心获得检索服务实例
fetch-registry: true
# 注册中心的服务地址
service-url:
defaultZone: http://${eureka.server.hostname}:${eureka.server.port}/eureka/
- 启动类配置。刷新验证:http://localhost:7000/
@EnableEurekaClient // 开启eureka client注册到server中
- 同样的方法注册article服务。通过eureka调用别的服务的接口。
// 注入服务发现,可以获得已经注册的服务相关信息
@Autowired
private DiscoveryClient discoveryClient;
String serviceId = "SERVICE-USER";
List<ServiceInstance> instanceList = discoveryClient.getInstances(serviceId);
ServiceInstance userService = instanceList.get(0);
String userServerUrlExecute
= "http://" + userService.getHost() +
":"
+ userService.getPort()
+ "/user/queryByIds?userIds=" + JsonUtils.objectToJson(idSet);
1.4 动态构建eureka集群
- 和上面创建工程eureka一样,修改配置文件。因为eureka服务之前需要通信,所以需要
defaultZone配置别的节点的地址即可。
eureka:
instance:
# 集群中每个eureka的名字都是唯一的
hostname: eureka-cluster-${server.port}
other-node-port2: ${p2:7002}
other-node-port3: ${p3:7003}
client:
# register-with-eureka: false
# fetch-registry: false
# 单实例配置自己的服务地址,高可用集群则配置多个地址
service-url:
defaultZone: http://eureka-cluster-${eureka.other-node-port2}:${eureka.other-node-port2}/eureka/,http://eureka-cluster-${eureka.other-node-port3}:${eureka.other-node-port3}/eureka/
server:
enable-self-preservation: false # 关闭eureka的自我保护功能
eviction-interval-timer-in-ms: 5000 # 清理无效节点的时间,可以缩短为5s,默认60s
- 通过配置启动JVM数,启动三个实例。eureka集群搭建就可以启动起来。
- 注册别的服务到eureka集群中。这里要注册三个服务上去。如果要启用多个客户端用动态传入port的方式启动多个服务。默认轮询的访问客户端。
eureka:
# 自定义eureka server的信息
server:
hostname: eureka
port: 7000
client:
# 所有的微服务都必须注册到eureka中
register-with-eureka: true
# 从注册中心获得检索服务实例
fetch-registry: true
# 注册中心的服务地址
service-url:
# defaultZone: http://${eureka.server.hostname}:${eureka.server.port}/eureka/
defaultZone: http://eureka-cluster-7001:7001/eureka/,http://eureka-cluster-7002:7002/eureka/,http://eureka-cluster-7003:7003/eureka/
instance:
lease-renewal-interval-in-seconds: 3 # 调整微服务(eureka client)和注册中心(eureka server)的心跳时间
lease-expiration-duration-in-seconds: 5 # eureka 举例最近的一次心跳等待提出的时间,默认90s
1.5 eureka自我保护功能
- 默认自我保护功能是开启的,它可以防止网络中断导致客户端连接不上服务端,但是各个服务都能正常运行。这时候开启自我保护功能可以防止eueka下掉客户端。
- 开发环境可以关闭,生产环境可以开一下自我保护功能。
server:
enable-self-preservation: false # 关闭eureka的自我保护功能
eviction-interval-timer-in-ms: 5000 # 清理无效节点的时间,可以缩短为5s,默认60s
- 客户端的心跳时间配置。
instance:
lease-renewal-interval-in-seconds: 3 # 调整微服务(eureka client)和注册中心(eureka server)的心跳时间
lease-expiration-duration-in-seconds: 5 # eureka 举例最近的一次心跳等待提出的时间,默认90s
第二节 Ribbon和feign
2.1 Ribbon介绍
- 服务间通信的负载均衡工具,提供完善的超时重试机制。
- Ribbon它是一种客户端的负载均衡,Nginx是常见的服务端的负载均衡。它本身就存在于eureka的客户端包中,maven中可见。
com.imooc.api.config.CloudConfig加上@LoadBalanced实现默认轮询的调用。
@Bean
@LoadBalanced // 默认轮询的调用
public RestTemplate restTemplate() {
return new RestTemplate(new OkHttp3ClientHttpRequestFactory());
}
2.2 自定义负载规则
- api服务中
com.rule.MyRule。这个包不要被启动类扫描到。 - 客户端调用使用这个规则,方法一。article启动类上加注解
package com.rule;
import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RandomRule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MyRule {
@Bean
public IRule iRule() {
return new RandomRule();
}
}
@RibbonClient(name = "SERVICE-USER", configuration = MyRule.class)
- 客户端调用使用这个规则,方法二。直接在application.yaml配置
SERVICE-USER:
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
2.3 ribbon重试机制
- api模块中导入依赖。
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
- 重试策略配置,application.yaml配置。防止网络抖动。
ribbon:
ConnectTimeout: 5000 # 创建连接的超时时间,单位:ms
ReadTimeout: 5000 # 在连接创建好以后,调用接口的超时时间,单位:ms
MaxAutoRetries: 1 # 最大重试次数
MaxAutoRetriesNextServer: 2 # 切换到下个微服务实例的重试次数
# 当请求到某个微服务5s,超时后会进行重试,先重试连接自己当前的这个实例
# 如果当前重试失败1次,则会切换到访问集群中的下一个微服务实例,切换最大为2次
logging:
level:
com.imooc.api.controller.user.UserControllerApi: debug
# root: debug
2.4 feign简化服务间调用
- api中导入依赖。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
- 调用端article服务启动类中加入注解。
@EnableFeignClients({"com.imooc"})
- 被调用端User的api的上方,加入注解。
// 别名叫做service-use
// @FeignClient(value = "service-user")
@FeignClient(value = MyServiceList.SERVICE_USER)
- article具体调用的地方注入并使用。
@Autowired
private UserControllerApi userControllerApi;
// 发起远程调用,获得用户的基本信息
private List<AppUserVO> getPublisherList(Set idSet) {
GraceJSONResult bodyResult = userControllerApi.queryByIds(JsonUtils.objectToJson(idSet));
List<AppUserVO> publisherList = null;
if (bodyResult.getStatus() == 200) {
String userJson = JsonUtils.objectToJson(bodyResult.getData());
publisherList = JsonUtils.jsonToList(userJson, AppUserVO.class);
} else {
publisherList = new ArrayList<>();
}
return publisherList;
}
- feign的日志调试开启。
logging:
level:
com.imooc.api.controller.user.UserControllerApi: debug
# root: debug
# 配置feign
feign:
client:
config:
# 配置服务提供方的名称
service-user:
loggerLevel: FULL
- 统一验证处理。这里注意如果使用feign在一个方法中不能写两个以上的对象,因为对feign来说就是传了俩个body。如下面所示。
@FeignClient(value = MyServiceList.SERVICE_USER)
@ApiOperation(value = "完善修改用户信息", notes = "完善修改用户信息", httpMethod = "POST")
@PostMapping("/updateUserInfo")
public GraceJSONResult updateUserInfo(@RequestBody @Valid UpdateUserInfoBO updateUserInfoBO,
BindingResult result);
- 对这种方法改造一下。
com.imooc.exception.GraceExceptionHandler这里捕获这个参数验证异常同一处理。
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseBody
public GraceJSONResult returnException(MethodArgumentNotValidException e) {
BindingResult result = e.getBindingResult();
Map<String, String> map = getErrors(result);
return GraceJSONResult.errorMap(map);
}
public Map<String, String> getErrors(BindingResult result) {
Map<String, String> map = new HashMap<>();
List<FieldError> errorList = result.getFieldErrors();
for (FieldError error : errorList) {
// 发送验证错误的时候所对应的某个属性
String field = error.getField();
// 验证的错误消息
String msg = error.getDefaultMessage();
map.put(field, msg);
}
return map;
}
- 这里把第二个验证的参数去掉即可。
@ApiOperation(value = "完善修改用户信息", notes = "完善修改用户信息", httpMethod = "POST")
@PostMapping("/updateUserInfo")
public GraceJSONResult updateUserInfo(@RequestBody @Valid UpdateUserInfoBO updateUserInfoBO);
第三节 hystrix熔断和网关zuul
3.1 hystrix断路器
- 提供容错机制,避免微服务系统雪崩。
- api中引入依赖。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
- 服务提供者的接口上加上熔断的注解。一旦异常去请求回调函数
queryByIdsFallback
@HystrixCommand(fallbackMethod = "queryByIdsFallback")
- 服务提供者启动类上加上熔断的注解。
@EnableCircuitBreaker // 开启hytrix的熔断机制
- 如果是超时异常可以在application.yml中加入配置。超过两秒进入降级。
# 配置hystrix
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 2000 # 设置hystrix超时时间,超过2秒触发降级
circuitBreaker: # 配置断路器
enabled: true
requestVolumeThreshold: 10 # 触发熔断最小请求次数,默认:20
sleepWindowInMilliseconds: 15000 # 熔断后过几秒后尝试半开状态(请求重试),默认:5s
errorThresholdPercentage: 50 # 触发熔断的失败率(异常率/阈值),默认:50
- 全局降级,如果方法比较多,每个方法都去加一个这个回调函数很麻烦。可以在服务提供者controll上加一个默认的回调函数。
defaultFallbackfunc写在当前controller中。
@DefaultProperties(defaultFallback = "defaultFallbackfunc")
@HystrixCommand // 默认就是调用"defaultFallbackfunc"方法
- 服务调用者降级。application.yaml中配置增加。如果服务端全挂了,客户端降级就派上用场了。
hystrix:
enabled: true # 打开feign客户端的内置hystrix
- 在服务调用的调用者上加上注解。
UserControllerFactoryFallback是自定义的回调函数。
@FeignClient(value = MyServiceList.SERVICE_USER, fallbackFactory = UserControllerFactoryFallback.class)
com.imooc.api.controller.user.fallbacks.UserControllerFactoryFallback内容如下。
package com.imooc.api.controller.user.fallbacks;
import com.imooc.api.controller.user.UserControllerApi;
import com.imooc.grace.result.GraceJSONResult;
import com.imooc.grace.result.ResponseStatusEnum;
import com.imooc.pojo.bo.UpdateUserInfoBO;
import com.imooc.pojo.vo.AppUserVO;
import feign.hystrix.FallbackFactory;
import org.springframework.stereotype.Component;
import javax.validation.Valid;
import java.util.ArrayList;
import java.util.List;
@Component
public class UserControllerFactoryFallback
implements FallbackFactory<UserControllerApi> {
@Override
public UserControllerApi create(Throwable throwable) {
return new UserControllerApi() {
@Override
public GraceJSONResult getUserInfo(String userId) {
return GraceJSONResult.errorCustom(ResponseStatusEnum.SYSTEM_ERROR_FEIGN);
}
@Override
public GraceJSONResult getAccountInfo(String userId) {
return GraceJSONResult.errorCustom(ResponseStatusEnum.SYSTEM_ERROR_FEIGN);
}
@Override
public GraceJSONResult updateUserInfo(@Valid UpdateUserInfoBO updateUserInfoBO) {
return GraceJSONResult.errorCustom(ResponseStatusEnum.SYSTEM_ERROR_FEIGN);
}
@Override
public GraceJSONResult queryByIds(String userIds) {
System.out.println("进入客户端(服务调用者)的降级方法");
List<AppUserVO> publisherList = new ArrayList<>();
return GraceJSONResult.ok(publisherList);
}
};
}
}
3.2 zuul微服务网关
- 微服务的网关,可以实现动态路由、过滤器等功能。
- 重新创建
springcloud-zuul-server模块。 - api服务中添加依赖。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
- springcloud-zuul-server启动类注解。
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class,
MongoAutoConfiguration.class})
@ComponentScan(basePackages = {"com.imooc", "org.n3r.idworker"})
@EnableEurekaClient
//@EnableZuulServer
@EnableZuulProxy // @EnableZuulProxy是@EnableZuulServer的一个增强升级版,当zuul和eureka、ribbon等组件共同使用,则使用增强版即可
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
- application.yaml的zuul配置。这里地址写死的,如果变动要修改会比较麻烦。
zuul:
routes:
service-article: # 配置微服务的路由id,微服务的实例id
path: /service-article/** # 请求路径(前缀)
url: http://192.168.1.2:8001 # 请求转发到指定的微服务所在的ip地址
prefix: /api
- 可以通过eureka的服务,请求实例。
zuul:
routes:
service-article: # 配置微服务的路由id,微服务的实例id
path: /service-article/** # 请求路径(前缀)
service-id: service-article # 请求转发的微服务实例id
prefix: /api # 请求前缀
- 简化上面路径**由于路由id和微服务实例id相同,我们可以简化转发的配置*
# 路由规则: http://[网关地址]:[端口号]/[prefix]/[微服务实例id]/[请求地址路径]
zuul:
routes:
# 由于路由id和微服务实例id相同,我们可以简化转发的配置
service-article: /service-article/**
# service-article: # 配置微服务的路由id,微服务的实例id
# path: /service-article/** # 请求路径(前缀)
# service-id: service-article # 请求转发的微服务实例id
# url: http://192.168.1.2:8001 # 请求转发到指定的微服务所在的ip地址
prefix: /api # 请求前缀
3.3 zuul自定义过滤器
com.imooc.zuul.filters.MyFilter
package com.imooc.zuul.filters;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.exception.ZuulException;
import org.springframework.stereotype.Component;
/**
* 构建zuul的自定义过滤器
*/
@Component
public class MyFilter extends ZuulFilter {
/**
* 定义过滤器的类型
* pre: 在请求被路由之前执行
* route: 在路由请求的时候执行
* post: 请求路由以后执行
* error: 处理请求时发生错误的时候执行
* @return
*/
@Override
public String filterType() {
return "pre";
}
/**
* 过滤器执行的顺序,配置多个有顺序的过滤
* 执行顺序从小到大
* @return
*/
@Override
public int filterOrder() {
return 1;
}
/**
* 是否开启过滤器
* true:开启
* false:禁用
* @return
*/
@Override
public boolean shouldFilter() {
return true;
}
/**
* 过滤器的业务实现
* @return
* @throws ZuulException
*/
@Override
public Object run() throws ZuulException {
System.out.println("display pre zuul filter...");
return null; // 没有意义可以不用管。
}
}
3.4 限制ip黑名单的频繁访问
- 首先appliction.yaml中配置。
# ip请求限制的参数配置
blackIp:
continueCounts: ${counts:10} # ip连续请求的次数
timeInterval: ${interval:10} # ip判断的事件间隔,单位:秒
limitTimes: ${times:15} # 限制的事件,单位:秒
- 自定义过滤器实现
com.imooc.zuul.filters.BlackIPFilter
package com.imooc.zuul.filters;
import com.imooc.grace.result.GraceJSONResult;
import com.imooc.grace.result.ResponseStatusEnum;
import com.imooc.utils.IPUtil;
import com.imooc.utils.JsonUtils;
import com.imooc.utils.RedisOperator;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
@Component
@RefreshScope
public class BlackIPFilter extends ZuulFilter {
@Value("${blackIp.continueCounts}")
public Integer continueCounts;
@Value("${blackIp.timeInterval}")
public Integer timeInterval;
@Value("${blackIp.limitTimes}")
public Integer limitTimes;
@Autowired
private RedisOperator redis;
@Override
public String filterType() {
return "pre";
}
@Override
public int filterOrder() {
return 2;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() throws ZuulException {
System.out.println("执行【ip黑名单】过滤器...");
System.out.println("continueCounts: " + continueCounts);
System.out.println("timeInterval: " + timeInterval);
System.out.println("limitTimes: " + limitTimes);
// 获得上下文对象
RequestContext context = RequestContext.getCurrentContext();
HttpServletRequest request = context.getRequest();
// 获得ip
String ip = IPUtil.getRequestIp(request);
/**
* 需求:
* 判断ip在10秒内的请求次数是否超过10次
* 如果超过,则限制这个ip访问15秒,15秒以后再放行
*/
final String ipRedisKey = "zuul-ip:" + ip;
final String ipRedisLimitKey = "zuul-ip-limit:" + ip;
// 获得当前ip这个key的剩余时间
long limitLeftTime = redis.ttl(ipRedisLimitKey);
// 如果当前限制ip的key还存在剩余时间,说明这个ip不能访问,继续等待
if (limitLeftTime > 0) {
stopRequest(context);
return null;
}
// 在redis中累加ip的请求访问次数
long requestCounts = redis.increment(ipRedisKey, 1);
// 从0开始计算请求次数,初期访问为1,则设置过期时间,也就是连续请求的间隔时间
if (requestCounts == 1) {
redis.expire(ipRedisKey, timeInterval);
}
// 如果还能取得请求次数,说明用户连续请求的次数落在10秒内
// 一旦请求次数超过了连续访问的次数,则需要限制这个ip的访问
if (requestCounts > continueCounts) {
// 限制ip的访问时间
redis.set(ipRedisLimitKey, ipRedisLimitKey, limitTimes);
stopRequest(context);
}
return null;
}
private void stopRequest(RequestContext context) {
// 停止zuul继续向下路由,禁止请求通信
context.setSendZuulResponse(false);
context.setResponseStatusCode(200);
String result = JsonUtils.objectToJson(
GraceJSONResult.errorCustom(
ResponseStatusEnum.SYSTEM_ERROR_ZUUL));
context.setResponseBody(result);
context.getResponse().setCharacterEncoding("utf-8");
context.getResponse().setContentType(MediaType.APPLICATION_JSON_VALUE);
}
}
第四节 配置中心config和总线BUS
4.1 分布式配置中心
- SpringCloud Config为所有服务提供统一的配置管理服务
- 优点:统一管理配置、管理不同环境下的配置和动态调整配置
- 新建配置中心服务
springcloud-config,启动配置注解。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class,
MongoAutoConfiguration.class})
@ComponentScan(basePackages = {"com.imooc", "org.n3r.idworker"})
@EnableEurekaClient
@EnableConfigServer
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
- 从git中获取配置文件。直接访问:192.168.44.128:7080/zuul-dev.yml 查看配置
spring:
application:
name: springcloud-config
cloud:
config:
server:
git:
uri: https://github.com/qnhyn/imooc-news-config.git
- application.yaml属于用户层次的配置文件,bootstrap.yaml是系统级的配置文件。优先级比application.yaml更高。
- 配置客户端获得配置文件。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
spring:
application:
name: springcloud-zuul-server
cloud:
config:
label: master # 代表配置中心git的分支
name: zuul
profile: prod # 环境变量
# uri: http://192.168.1.2:7080
discovery:
enabled: true
service-id: springcloud-config
- 动态刷新git配置。zuul中引入actuator
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
# 配置动态刷新git配置的路径终端请求地址
management:
endpoints:
web:
exposure:
include: refresh
- 配置完毕后,需要主动发起请求刷新配置。http://localhost7070/actuator/refresh 需要配置
@RefreshScope注解,开启自刷新功能。
@RefreshScope
public class BlackIPFilter extends ZuulFilter
4.2 消息总线BUS
- 上面的config的问题:手动刷新与业务耦合。N个微服务端需要N次手动刷新。
- SpringCloud Bus 为SpringCloud Config提供增益buff,可以实现配置自动刷新
- git发生配置更改config就会根据消息总线推给我们的各个服务进行更改。Bus支持RabbitMQ和kafka
- config服务引入bus配置文件,zuul-server服务也需要引入这个。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
- 配置zuul-server服务和config服务的rabbitmq
rabbitmq:
host: 192.168.44.128
port: 5672
username: admin
password: 123456
virtual-host: imooc-news-dev
- config服务配置application.yaml
# 配置动态刷新git配置的路径终端请求地址
management:
endpoints:
web:
exposure:
include: "*"
- 这里直接请求:http://localhost7070/actuator/bus-refresh/ 刷新配置端 再次请求就是最新的配置了。全量刷新。
- 精确刷线:http://localhost7070/actuator/bus-refresh/{微服务的实例id}:{port} 只刷新一个服务的配置。
第五节 消息驱动和链路追踪
5.1 Spring Cloud Stream
- 统一封装消息的服务框架
- RabbitMQ, RocketMQ, Kafka,ActiveMQ,ZeroMQ … 每种消息队列如果都要学习就会很繁琐。通过 Spring Cloud Stream可以把这些MQ整合到一起,提供一个统一的接口去调用。现在支持RabbitMQ和Kafka
- Stream 消息模型
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-idxhYiJh-1654791009866)(.\printscreen\6节_1.png)] - 引入stream依赖。article客户端调用user服务端。这两个服务都需要引用。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-stream-rabbit</artifactId>
</dependency>
- article和user都配置一下。
# article
cloud:
stream:
bindings: # 绑定通道和交换机
myOutput: # 定义生产者的通道
# 自定义交换机的名字,也就是代码里构建的消息,交给底层mq的交换机
destination: streamExchange
myInput: # 定义消费者的通道
# 自定义交换机的名字,也就是消息从底层mq输入到消费端进行消费
destination: streamExchange
group: boys
# user
cloud:
stream:
bindings: # 绑定通道和交换机
myOutput: # 定义生产者的通道
# 自定义交换机的名字,也就是代码里构建的消息,交给底层mq的交换机
destination: streamExchange
myInput: # 定义消费者的通道
# 自定义交换机的名字,也就是消息从底层mq输入到消费端进行消费
destination: streamExchange
group: girls
- rticle和user都配置新建通道channel的接口
com.imooc.article.stream.MyStreamChannel
/**
* 声明构建通道channel
*/
@Component
public interface MyStreamChannel {
String OUTPUT = "myOutput";
String INPUT = "myInput";
@Output(MyStreamChannel.OUTPUT)
MessageChannel output();
@Input(MyStreamChannel.INPUT)
SubscribableChannel input();
}
com.imooc.article.stream.StreamService生产者用来发送消息和它的实现com.imooc.article.stream.StreamServiceImpl通过@EnableBinding(MyStreamChannel.class)绑定。
/**
* 开启绑定器
* 绑定通道channel
*/
@Component
@EnableBinding(MyStreamChannel.class)
public class StreamServiceImpl implements StreamService {
@Autowired
private MyStreamChannel myStreamChannel;
@Override
public void sendMsg() {
AppUser user = new AppUser();
user.setId("10101");
user.setNickname("imooc");
// 消息通过绑定器发送给mq
myStreamChannel.output()
.send(MessageBuilder.withPayload(user).build());
}
@Override
public void eat(String dumpling) {
myStreamChannel.output()
.send(MessageBuilder.withPayload(dumpling).build());
}
}
- 消费者绑定
com.imooc.article.stream.MyStreamConsumer
/**
* 构建消费端
*/
@Component
@EnableBinding(MyStreamChannel.class)
public class MyStreamConsumer {
/**
* 监听并且实现消息的消费和相关业务处理
*/
// @StreamListener(MyStreamChannel.INPUT)
// public void receiveMsg(AppUser user) {
// System.out.println(user.toString());
// }
@StreamListener(MyStreamChannel.INPUT)
public void receiveMsg(String dumpling) {
System.out.println(dumpling);
}
}
- 控制器测试。
com.imooc.article.controller.HelloController
@Autowired
private StreamService streamService;
@GetMapping("/stream")
public Object stream() {
// streamService.sendMsg();
for (int i = 0 ; i < 10 ; i ++ ) {
streamService.eat("我吃了第" + (i+1) + "只饺子~");
}
return "ok~~!!!";
}
- 启动article和启动俩个user客户端,进行测试。localhost:8001/producer/stream 。因为配置了group所以开启了持久化,重启user客户端会继续消费。
5.2 链路追踪Sleuth
- 贯穿整个分布式微服务系统中,追踪一个请求的过程。zipkin可视化控制面板
- 下载服务端zipkin,启动访问:localhost:9411/zipkin
java -jar zipkin-server-2.12.9-exec.jar
- 客户端引入依赖。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
</dependency>
zipkin:
# 配置zipkin采集的服务地址,数据会发送到这里
base-url: http://192.168.1.2:9411/
sender:
# 数据采集的传输通信方式,web http的形式
type: web
sleuth:
sampler:
# 数据采样比例(百分数),0~1
probability: 1
- 一般企业中用到的链路追踪skywalking比较多。

被折叠的 条评论
为什么被折叠?



