SpringCloud2020学习笔记
技术选型
依赖 | 版本 |
---|---|
SpringCloud | Hoxton.SR1 |
SpringBoot | 2.2.2.RELEASE |
SpringCloud Alibaba | 2.1.0.RELEASE |
工具汇总
名称 | 版本 | 备注 | 下载地址 |
---|---|---|---|
Java | 8 | 官网下载 | |
Maven | 3.5+ | 官网下载 | |
MySQL | 5.7+ | 官网下载 | |
IntelliJ IDEA | 2019.3.3 | 官网下载 | |
zookeeper | 3.4.14 | 官网下载 | |
nacos-server | 1.2.0 | nacos服务端 | Github下载 |
consul-server | 1.7.2 | consul服务端 | 官网下载 |
zipkin-server | 2.12.9 | zipkin链路监控服务端 | 第三方下载 |
postman | 7.20.1 | 接口请求测试 | 官网下载 |
Seata | 0.9.0 | 分布式事务服务端 | 官网下载 |
项目中使用的数据库文件在项目根目录resouces/SQL/下,阶段代码直接查看提交记录
项目结构说明
项目名 | 用途 | 所用技术 |
---|---|---|
cloud-api-commons | 公共代码部分(返回结果集、实体等) | \ |
cloud-consumer-consul-order80 | 消费方通过Consul调用服务方 | 注册中心(Consul)、远程调用(RestTemplate) |
cloud-consumer-feign-order80 | 消费方通过feign远程调用服务方 | 服务调用(Openfeign)、注册中心(EurekaClient) |
cloud-consumer-order80 | 消费方通过Eureka调用服务方等示例 | 注册中心(Eureka)、远程调用(RestTemplate)、链路追踪(Sleuth) |
cloud-consumer-zk-order80 | 消费方通过Zk调用服务方 | 注册中心(Zk)、远程调用(RestTemplate) |
cloud-eureka-server7001 | Eureka服务端1 | 注册中心(EurekaServer) |
cloud-eureka-server7002 | Eureka服务端2 | 注册中心(EurekaServer) |
cloud-provider-consul-payment8006 | 服务方通过Consul注册服务 | 注册中心(Consul) |
cloud-provider-payment8001 | 服务方通过Eureka注册服务 | 注册中心(Euraka)、链路追踪(Sleuth) |
cloud-provider-payment8002 | 服务方通过Eureka注册服务 | 注册中心(Euraka) |
cloud-provider-zk-payment8004 | 服务方通过Zk注册服务 | 注册中心(Zk) |
cloud-provider-hystrix-payment8001 | 服务方通过Hystrix服务降级和熔断 | 服务降级和熔断(Hystrix)、服务监控(Actuator) |
cloud-consumer-feign-hystrix-order80 | 消费方消费方通过feign远程调用服务方带降级和服务 | 服务降级和熔断(Hystrix)、服务调用(Openfeign) |
cloud-consumer-hystrix-dashboard9001 | Hystrix监控仪表盘 | 服务监控(Hystrix) |
cloud-gateway-gateway9527 | 网关服务GateWay | 网关(GateWay) |
cloud-config-center-3344 | 配置中心服务端 | 配置中心(Config-Server)、服务总线(BUS) |
cloud-config-client-3355 | 配置中心客户端1 | 配置中心(Config)、服务总线(BUS) |
cloud-config-client-3356 | 配置中心客户端1 | 配置中心(Config)、服务总线(BUS) |
cloud-stream-provider-rabbitmq8801 | 消息驱动发送方 | 消息驱动(Stream) |
cloud-stream-provider-rabbitmq8802 | 消息驱动接受方1 | 消息驱动(Stream) |
cloud-stream-consumer-rabbitmq8803 | 消息驱动接受方2 | 消息驱动(Stream) |
cloudalibaba-config-nacos-client3377 | 配置中心客户端Nacos | 配置中心(Nacos)、服务注册(Nacos) |
cloudalibaba-provider-nacos-payment9001 | 服务方通过Nacos注册服务1 | 服务注册(Nacos) |
cloudalibaba-provider-nacos-payment9002 | 服务方通过Nacos注册服务2 | 服务注册(Nacos) |
cloudalibaba-provider-nacos-payment9003 | 服务方通过Nacos注册服务模拟查询1 | 服务注册(Nacos) |
cloudalibaba-provider-nacos-payment9004 | 服务方通过Nacos注册服务模拟查询2 | 服务注册(Nacos) |
cloudalibaba-sentinel-service8401 | 服务限流和熔断Sentinel示例 | 服务注册(Nacos)、服务限流和熔断(Sentinel) |
cloudalibaba-consumer-nacos-order84 | 消费方通过整合Feign和Sentinel | 服务注册(Nacos)、服务限流和熔断(Sentinel)、服务调用(Openfeign)、负载均衡(Rabbin) |
cloudalibaba-seata-order-service12001 | 分布式事务商品下单微服务 | 分布式事务(Seata)、服务注册(Nacos)、服务调用(Openfeign)、负载均衡(Rabbin) |
cloudalibaba-seata-storage-service12002 | 分布式事务库存微服务 | 分布式事务(Seata)、服务注册(Nacos)、服务调用(Openfeign)、负载均衡(Rabbin) |
cloudalibaba-seata-account-service12003 | 分布式事务账户微服务 | 分布式事务(Seata)、服务注册(Nacos)、服务调用(Openfeign)、负载均衡(Rabbin) |
知识点整理
一、服务注册与发现
1、使用Eureka服务端
1)导入依赖
<!--eureka-server-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<!--热部署推荐一起-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
2)application.yml示例
server:
port: 7001
eureka:
instance:
hostname: eureka7001.com #Eureka服务端的实例名称
client:
#false表示不向注册中心中注册自己
register-with-eureka: false
#false表示自己就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
fetch-registry: false
service-url:
#设置与Eureka Server之间交互的地址,查询服务和注册服务都需要依赖这个地址
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ #单机版,指向自己
#defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka #集群版
server:
#关闭Eureka自我保护机制,保证不可用服务被及时剔除
enable-self-preservation: false
eviction-interval-timer-in-ms: 2000
3)启动类添加注解@EnableEurekaServer
@SpringBootApplication
@EnableEurekaServer
public class EurekaMain7001 {
public static void main(String[] args) {
SpringApplication.run(EurekaMain7001.class, args);
}
}
4)验证
对应项目:cloud-eureka-server7001、cloud-eureka-server7002
2、使用Eureka客户端
1)导入依赖
<!--引入eureka客户端eureka-client -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
2)application.yml示例
server:
port: 8001
spring:
application:
name: cloud-payment-service
eureka:
client:
#表示是否将自己注册进EurekaServer,默认为true
register-with-eureka: true
#是否从EurekaServer中抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
fetchRegistry: true
service-url:
defaultZone: http://localhost:7001/eureka #单机版
#defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka #集群版
instance:
instance-id: payment8001 #修改Status名称
prefer-ip-address: true
#Eureka客户端向服务端发送心跳的时间间隔,单位为秒(默认30)
lease-renewal-interval-in-seconds: 10
#Eureka服务端在收到最后一次心跳后等待的时间上限,单位为秒(默认90),超时剔除服务
lease-expiration-duration-in-seconds: 60
3)启动类添加注解@EnableDiscoveryClient(推荐)或者@EnableEurekaClient
@SpringBootApplication
@EnableDiscoveryClient //Discovery服务发现
public class PaymentMain8001 {
public static void main(String[] args) {
SpringApplication.run(PaymentMain8001.class, args);
}
}
对应项目:cloud-provider-payment8001、cloud-provider-payment8002等
3、使用Zookeeper注册服务
1)导入依赖
<!-- SpringBoot整合zookeeper客户端 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
<!-- 先排除自带的zookeeper -->
<exclusions>
<exclusion>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- 添加本机zookeeper3.4.14版本 -->
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.14</version>
</dependency>
2)application.yml示例
server:
port: 8004
#服务别名——注册到zookeeper注册中心的名称
spring:
application:
name: cloud-provider-payment
cloud:
zookeeper:
connect-string: 127.0.0.1:2181
3)启动类添加注解@EnableDiscoveryClient
@SpringBootApplication
@EnableDiscoveryClient //该注解用于向使用zookeeper作为注册中心时注册服务
public class ZkPaymentMain8004 {
public static void main(String[] args) {
SpringApplication.run(ZkPaymentMain8004.class, args);
}
}
对应项目:cloud-provider-zk-payment8004
4、使用Consul注册服务
1)导入依赖
<!-- consul软件官方下载:https://www.consul.io/downloads.html
开发模式启动:consul agent -dev
验证:浏览器访问 http://localhost:8500
若提示:
Consul returned an error. You may have visited a URL that is loading an unknown resource,so you can try going back to the root or try re-submitting your ACL Token/SecretID by going back to ACLs.
错误请使用其他浏览器访问试试
-->
<!--consul版服务提供方-->
<!--SpringCloud consul-server -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
2)application.yml示例
server:
port: 8006
spring:
application:
name: consul-provider-payment
#consul注册中心地址
cloud:
consul:
host: localhost
port: 8500
discovery:
#hostname: 127.0.0.1
service-name: ${spring.application.name}
3)启动类添加注解@EnableDiscoveryClient
@SpringBootApplication
@EnableDiscoveryClient
public class ConsulPaymentMain8006 {
public static void main(String[] args) {
SpringApplication.run(ConsulPaymentMain8006.class, args);
}
}
对应项目:cloud-provider-consul-payment8006
二、负载均衡调用
1、使用Ribbon负载均衡
默认eureka集成了rabbin,配置即可使用,默认是轮训策略。
1)新建规则类MySelfRule.java
/**
* MySelfRule
*
* @author lcry
* 自定义Ribbon默认轮训规则类
*/
@Configuration
public class MySelfRule {
@Bean
public IRule myRule() {
return new RandomRule();//定义为随机
}
}
IRule实现类有如下:
com.netflix.loadbalancer.RoundRobinRule:轮询
com.netflix.loadbalancer.RandomRule:随机
com.netflix.loadbalancer.RetryRule:先按照RoundRobinRule的策略获取服务,如果获取服务失败则在指定时间内进行重试,获取可用的服务
WeightedResponseTimeRule:对RoundRobinRule的扩展,响应速度越快的实例选择权重越多大,越容易被选择
BestAvailableRule:会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务
AvailabilityFilteringRule:先过滤掉故障实例,再选择并发较小的实例
ZoneAvoidanceRule:默认规则,复合判断server所在区域的性能和server的可用性选择服务器
2)启动类添加注解@RibbonClient(name = "CLOUD-PAYMENT-SERVICE", configuration = MySelfRule.class)//启用Ribbon
@SpringBootApplication
@EnableDiscoveryClient
@RibbonClient(name = "CLOUD-PAYMENT-SERVICE", configuration = MySelfRule.class)//启用Ribbon
public class OrderMain80 {
public static void main(String[] args) {
SpringApplication.run(OrderMain80.class, args);
}
}
若使用restTemplate直接使用轮训规则,可以直接在配置类上@LoadBalanced注解
/**
* ApplicationContextConfig
*
* @author lcry
* @date 2020/03/14 12:05
* 配置RestTemplate
*/
@Configuration
public class ApplicationContextConfig {
@Bean //相当于Spring中applicationContext.xml中<bean id="" class="">
@LoadBalanced //使用此注解赋予RestTemplate负载均衡的能力,测试自定义算法注释掉
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
}
对应项目:cloud-consumer-order80
2、使用OpenFeign
1)导入依赖
<!-- openfeign -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
2)application.yml示例
server:
port: 80
eureka:
client:
register-with-eureka: false
service-url:
defaultZone: http://localhost:7001/eureka #单机版
#defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka #集群版
#OpenFeign的超时控制配置:默认只等待1秒,超时则报错
#设置Feign客户端超时时间(OpenFeign默认支持Ribbon)
ribbon:
#指的是建立连接所用的时间,适用于网络状况正常的情况下,两端连接所用的时间
ReadTimeout: 5000
#指的是建立连接后从服务器读取到可用资源所用的时间
connectTimeout: 5000
#开启Feign的日志客户端
logging:
level:
#Feign日志以什么级别监听哪个接口
com.lcry.springcloud.service.PaymentFeignService: debug
3)启动类添加注解@EnableFeignClients
@SpringBootApplication
//启用Feign
@EnableFeignClients
public class FeignOrderMain80 {
public static void main(String[] args) {
SpringApplication.run(FeignOrderMain80.class, args);
}
}
4)配置日志打印信息 FeignConfig.java
/**
* FeignConfig
*
* @author lcry
* @date 2020/03/14 22:04
* Feign日志控制
*/
@Configuration
public class FeignConfig {
@Bean
Logger.Level feignLoggerLevel() {
/**
* 日志级别:
* NONE :不记录任何日志(默认)
* BASIC:仅记录请求方法、URL、响应状态代码以及执行时间
* HEADERS:记录BASIC级别的基础上,记录请求和响应的header
* FULL:记录请求和响应的header,body和元数据
*/
return Logger.Level.FULL;
}
}
5)远程调用服务
/**
* PaymentFeignService
*
* @author lcry
*/
@Component
@FeignClient(value = "CLOUD-PAYMENT-SERVICE") //需要调用的目标微服务应用名称
public interface PaymentFeignService {
//Feign接口中的方法要与目标服务中的Controller中的方法完全一致,可以直接copy
@GetMapping(value = "/payment/get/{id}")
public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id);
@GetMapping(value = "/payment/feign/timeout")
public String paymentFeignTimeout();
}
对应项目:cloud-consumer-feign-order80
三、服务熔断与降级
1、使用HyStrix熔断和降级
1)导入依赖
<!--hystrix-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
2)application.yml示例
server:
port: 8001
spring:
application:
name: cloud-provider-hystrix-payment
eureka:
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://127.0.0.1:7001/eureka
#defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
3)启动类添加注解@EnableHystrix
@SpringBootApplication
@EnableEurekaClient
@EnableHystrix //服务端开启Hystrix或者使用@EnableCircuitBreaker
public class HystrixPaymentMain8001 {
public static void main(String[] args) {
SpringApplication.run(HystrixPaymentMain8001.class, args);
}
}
4)服务降级示例代码,@HystrixCommand注解实现
/**
* 模拟超时3s
*
* @param id
* @return
*/
@Override
@HystrixCommand(fallbackMethod = "paymentInfo_TimeOutHandler", commandProperties = {
//超时5s或者异常直接降级
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "5000")
})
public String paymentInfo_TimeOut(Integer id) {
//int age = 10/0;
int timenum = 3;
try {
TimeUnit.SECONDS.sleep(timenum);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "线程池: " + Thread.currentThread().getName() + " id: " + id + "\t" + "O(∩_∩)O哈哈~" + " 耗时(秒): " + timenum;
}
public String paymentInfo_TimeOutHandler(Integer id) {
return "线程池: " + Thread.currentThread().getName() + " 8001系统繁忙或者运行报错,请稍后再试,id: " + id + "\t" + "o(╥﹏╥)o";
}
5)服务熔断示例代码
/**
* 以下内容属于服务熔断
*
* @param id
* @return
*/
@Override
@HystrixCommand(fallbackMethod = "paymentCircuitBreaker_fallback", commandProperties = {
@HystrixProperty(name = "circuitBreaker.enabled", value = "true"),// 是否开启断路器
@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "10"),// 请求次数
@HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "10000"), // 时间窗口期
@HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "60"),// 失败率达到多少后跳闸
})
public String paymentCircuitBreaker(@PathVariable("id") Integer id) {
if (id < 0) {
throw new RuntimeException("id 不能负数");
}
String serialNumber = IdUtil.simpleUUID();
return Thread.currentThread().getName() + "\t" + "调用成功,流水号: " + serialNumber;
}
public String paymentCircuitBreaker_fallback(@PathVariable("id") Integer id) {
return "id 不能负数,请稍后再试,/(ㄒoㄒ)/~~ id: " + id;
}
对应项目:cloud-provider-hystrix-payment8001、cloud-consumer-feign-order80
2、使用HystrixDashboard监控仪表盘
1)导入依赖
<!-- hystrix服务监控平台dashboard -->
<!-- 被监控服务一定要引入web和actuator两个依赖 -->
<!--
监控地址:http://ip:端口/hystrix
被监控填写:http://ip:端口/hystrix.stream
-->
<!-- hystrix-dashboard -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
2)application.yml示例
server:
port: 9001
3)启动类添加注解@EnableHystrixDashboard
/**
* HystrixDashboardMain9001
*
* @author lcry
*/
@SpringBootApplication
@EnableHystrixDashboard //启用注解
public class HystrixDashboardMain9001 {
public static void main(String[] args) {
SpringApplication.run(HystrixDashboardMain9001.class, args);
}
}
4)被监控端配置
-
引入依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
-
启动类注入
/** * 此配置是为了服务监控而配置,与服务容错本身无关,springcloud升级后的坑 * ServletRegistrationBean因为springboot的默认路径不是"/hystrix.stream", * 只要在自己的项目里配置上下面的servlet就可以了 */ @Bean public ServletRegistrationBean getServlet() { HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet(); ServletRegistrationBean registrationBean = new ServletRegistrationBean(streamServlet); registrationBean.setLoadOnStartup(1); registrationBean.addUrlMappings("/hystrix.stream"); registrationBean.setName("HystrixMetricsStreamServlet"); return registrationBean; }
5)验证
访问hystrix服务监控平台http://localhost:9001,被监控填写:http:// ip:端口/hystrix.stream
对应项目:cloud-consumer-hystrix-dashboard9001、cloud-provider-hystrix-payment8001
四、路由网关
1、使用Zuul
忽略,直接使用Gateway新一代网关
2、使用SpringCloud Gateway
1)导入依赖
<!--gateway-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!--eureka-client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!--引入自己定义的api调用包,可以使用Payment支付Entity-->
<dependency>
<groupId>com.lcry</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</version>
</dependency>
2)通过application.yml配置路由网关示例(推荐)
server:
port: 9527
spring:
application:
name: cloud-gateway
cloud:
gateway:
discovery:
locator:
enabled: true #开启从注册中心动态创建路由的功能,利用微服务名进行路由
routes:
- id: payment_route #payment_route #路由的ID,没有固定规则但要求唯一,建议配合服务名
#uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service #匹配后提供服务的路由地址
predicates:
- Path=/payment/get/** # 断言,路径相匹配的进行路由
- id: payment_route2 #payment_route #路由的ID,没有固定规则但要求唯一,建议配合服务名
#uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service #匹配后提供服务的路由地址
predicates:
- Path=/payment/lb/** # 断言,路径相匹配的进行路由
#- After=2020-02-21T15:51:37.485+08:00[Asia/Shanghai]
#- Cookie=username,zzyy
#- Header=X-Request-Id, \d+ # 请求头要有X-Request-Id属性并且值为整数的正则表达式
eureka:
instance:
hostname: cloud-gateway-service
client: #服务提供者provider注册进eureka服务列表内
service-url:
register-with-eureka: true
fetch-registry: true
defaultZone: http://localhost:7001/eureka
3)通过编码方式实现路由网关
/**
* 通过编码方式实现路由
*/
@Configuration
public class GateWayConfig {
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder routeLocatorBuilder) {
RouteLocatorBuilder.Builder routes = routeLocatorBuilder.routes();
routes.route("path_route_baidunews",
r -> r.path("/guonei")
.uri("http://news.baidu.com/guonei")).build();
return routes.build();
}
}
4)自定义过滤逻辑示例 MyLogGateWayFilter.java
/**
* 自定义过滤器示例-一般用于鉴权或者全局日志
*/
@Component
@Slf4j
public class MyLogGateWayFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
log.info("***********come in MyLogGateWayFilter: " + new Date());
//请求参数必须带username,并且不能为空
String uname = exchange.getRequest().getQueryParams().getFirst("username");
if (StrUtil.isEmpty(uname)) {
log.info("*******用户名为空,非法用户,o(╥﹏╥)o");
exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);
return exchange.getResponse().setComplete();
}
return chain.filter(exchange); //放行给下一条过滤链
}
@Override
public int getOrder() {
//执行顺序,越小越先执行
return 0;
}
}
对应项目:cloud-gateway-gateway9527
五、分布式配置中心
1、使用SpringCloud Config服务端
1)导入依赖
<!-- config-server依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
2)application.yml示例
server:
port: 3344
spring:
application:
name: cloud-config-center #注册进Eureka服务器的微服务名
cloud:
config:
server:
git:
uri: git@gitee.com:lcry/springcloud-config.git #git仓库地址
####搜索目录
search-paths:
- springcloud-config
####读取分支
label: master
#服务注册到eureka地址
eureka:
client:
service-url:
defaultZone: http://localhost:7001/eureka
3)启动类添加注解@EnableConfigServer
@SpringBootApplication
@EnableConfigServer //启用注册中心服务端
public class ConfigCenterMain3344 {
public static void main(String[] args) {
SpringApplication.run(ConfigCenterMain3344.class, args);
}
}
对应项目:cloud-config-center-3344
2、使用SpringCloud Config客户端
1)导入依赖
<!-- spring-config客户端依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
2)application.yml示例
server:
port: 3355
spring:
application:
name: config-client
cloud:
#Config客户端配置
config:
label: master #分支名称
name: config #配置文件名称
profile: dev #读取后缀名称 上述3个综合:master分支上config-dev.yml的配置文件被读取http://config-3344.com:3344/master/config-dev.yml
uri: http://localhost:3344 #配置中心地址server:
port: 3355
spring:
application:
name: config-client
cloud:
#Config客户端配置
config:
label: master #分支名称
name: config #配置文件名称
profile: dev #读取后缀名称 上述3个综合:master分支上config-dev.yml的配置文件被读取http://config-3344.com:3344/master/config-dev.yml
uri: http://localhost:3344 #配置中心地址
对应项目:cloud-config-client-3355、cloud-config-client-3356
六、消息总线
1、使用SpringCloud Bus刷新配置
1)导入依赖
<!--添加消息总线RabbitMQ支持-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
2)SpringCloud Config服务端application.yml示例
#rabbitmq相关配置
rabbitmq:
host: 192.168.179.150
port: 5672
username: admin
password: admin
#全局刷新POST请求 http://注册中心地址/actuator/bus-refresh
#定点通知:(只通知部分)http://注册中心地址/actuator/refresh/{服务名称:端口}
#如:http://localhost:3344/actuator/bus-refresh/config-client:3355
##rabbitmq相关配置,暴露bus刷新配置的端点
management:
endpoints: #暴露bus刷新配置的端点
web:
exposure:
include: 'bus-refresh'
3)SpringCloud Config客户端application.yml示例
#rabbitmq相关配置 15672是Web管理界面的端口;5672是MQ访问的端口
rabbitmq:
host: 192.168.179.150
port: 5672
username: admin
password: admin
# 手动刷新地址POST http://客户端访问地址/actuator/refresh
# curl -X POST "http://localhost:3355/actuator/refresh"
# 暴露监控端点
management:
endpoints:
web:
exposure:
include: "*"
4)刷新配置注解@RefreshScope
@RestController
@RefreshScope //刷新配置生效
public class ConfigClientController {
@Value("${server.port}")
private String serverport;
@Value("${config.info}")
private String configInfo;
// 直接获取配置文件
@GetMapping("/configInfo")
public String getConfigInfo() {
return configInfo + "服务端口:"+serverport;
}
}
对应项目:cloud-config-center-3344、cloud-config-center-3355、cloud-config-center-3356
七、消息驱动
1、使用Stream发送方
1)导入依赖
<!-- 引入stream-rabbit-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-stream-rabbit</artifactId>
</dependency>
2)application.yml示例
server:
port: 8801
spring:
application:
name: cloud-stream-provider
cloud:
stream:
binders: # 在此处配置要绑定的rabbitmq的服务信息;
defaultRabbit: # 表示定义的名称,用于于binding整合
type: rabbit # 消息组件类型
environment: # 设置rabbitmq的相关的环境配置
spring:
rabbitmq:
host: 192.168.179.150
port: 5672
username: admin
password: admin
bindings: # 服务的整合处理
output: # 这个名字是一个通道的名称
destination: studyExchange # 表示要使用的Exchange名称定义
content-type: application/json # 设置消息类型,本次为json,文本则设置“text/plain”
binder: defaultRabbit # 设置要绑定的消息服务的具体设置
eureka:
client: # 客户端进行Eureka注册的配置
service-url:
defaultZone: http://localhost:7001/eureka
instance:
lease-renewal-interval-in-seconds: 2 # 设置心跳的时间间隔(默认是30秒)
lease-expiration-duration-in-seconds: 5 # 如果现在超过了5秒的间隔(默认是90秒)
instance-id: cloud-stream-provider8801 # 在信息列表时显示主机名称
prefer-ip-address: true # 访问的路径变为IP地址
3)定义推送管道
/**
* //定义消息的推送管道
*/
@EnableBinding(Source.class) //定义消息的推送管道,生产者固定写法
@Slf4j
public class MessageProviderImpl implements IMessageProvider {
@Resource
private MessageChannel output; // 消息发送管道
@Override
public String send() {
String serial = UUID.randomUUID().toString();
output.send(MessageBuilder.withPayload(serial).build());
log.info(String.format("生成的流水号:{%s}", serial));
return serial;
}
}
对应项目:cloud-stream-provider-rabbitmq8801
2、使用Stream接受方
1)导入依赖
<!-- stream-rabbit -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-stream-rabbit</artifactId>
</dependency>
2)application.yml示例
server:
port: 8802
spring:
application:
name: cloud-stream-consumer
cloud:
stream:
binders: # 在此处配置要绑定的rabbitmq的服务信息;
defaultRabbit: # 表示定义的名称,用于于binding整合
type: rabbit # 消息组件类型
environment: # 设置rabbitmq的相关的环境配置
spring:
rabbitmq:
host: 192.168.179.150
port: 5672
username: admin
password: admin
bindings: # 服务的整合处理
input: # 这个名字是一个通道的名称
destination: studyExchange # 表示要使用的Exchange名称定义
content-type: application/json # 设置消息类型,本次为对象json,如果是文本则设置“text/plain”
binder: defaultRabbit # 设置要绑定的消息服务的具体设置
group: receiveclientA #分组避免重复消费和持久化问题
eureka:
client: # 客户端进行Eureka注册的配置
service-url:
defaultZone: http://localhost:7001/eureka
instance:
lease-renewal-interval-in-seconds: 2 # 设置心跳的时间间隔(默认是30秒)
lease-expiration-duration-in-seconds: 5 # 如果现在超过了5秒的间隔(默认是90秒)
instance-id: cloud-stream-consumer8802 # 在信息列表时显示主机名称
prefer-ip-address: true # 访问的路径变为IP地址
3)接收消息逻辑代码
@Component
@EnableBinding(Sink.class)
public class ReceiveMessageListenerController {
@Value("${server.port}")
private String serverPort;
@StreamListener(Sink.INPUT) //固定的写法,对应yml配置的通道名称
public void input(Message<String> message) {
System.out.println("消费者1号,----->接受到的消息: " + message.getPayload() + "\t port: " + serverPort);
}
}
对应项目:cloud-stream-consumer-rabbitmq8802、cloud-stream-consumer-rabbitmq8802
八、分布式链路追踪
1、使用sleuth客户端
1)导入依赖
<!--链路监控包含了sleuth+zipkin-->
<!--服务端下载地址:https://dl.bintray.com/openzipkin/maven/io/zipkin/java/zipkin-server/-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
</dependency>
<!--引入eureka客户端eureka-client -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
2)application.yml示例
spring:
application:
name: cloud-payment-service
#链路监控配置,配置链路监控服务端地址
zipkin:
base-url: http://localhost:9411
sleuth:
sampler:
#采样率值介于 0 到 1 之间,1 则表示全部采集
probability: 1
对应项目:cloud-provider-payment8001、cloud-consumer-order80
九、SpringCloud alibaba
1、使用Nacos服务注册
1)导入依赖
<!--SpringCloud ailibaba nacos -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
2)application.yml示例
server:
port: 9001
spring:
application:
name: nacos-payment-provider
cloud:
nacos:
discovery:
server-addr: localhost:8848 #配置Nacos地址,注册服务到Nacos
management:
endpoints:
web:
exposure:
include: '*'
3)启动类添加注解@EnableDiscoveryClient
/**
* NacosPaymentMain9001
*
* @author lcry
* @date 2020/03/16 18:03
*/
@EnableDiscoveryClient
@SpringBootApplication
public class NacosPaymentMain9001 {
public static void main(String[] args) {
SpringApplication.run(NacosPaymentMain9001.class,args);
}
}
对应项目:cloudalibaba-provider-nacos-payment9001、cloudalibaba-provider-nacos-payment9002
2、使用Nacos注册中心
1)导入依赖
<!-- nacos服务直接到官网下载以jar运行即可-->
<!--nacos-config-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!--nacos-discovery-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--web + actuator-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
2)application.yml示例
#激活配置文件选项
spring:
profiles:
active: dev # 表示开发环境
#active: test # 表示测试环境
#active: prod # 表示生产环境
# nacos配置
server:
port: 3377
spring:
application:
name: nacos-config-client
cloud:
nacos:
discovery:
server-addr: localhost:8848 #Nacos服务注册中心地址
config:
server-addr: localhost:8848 #Nacos作为配置中心地址
namespace: Test-NameSpace-HA1 #命名空间ID,nacos1.2可以自定义
group: DEV_GROUP #分组ID
file-extension: yaml #指定yaml格式的配置
#
#命名规范:
# ${spring.application.name}-${spring.profile.active}.${spring.cloud.nacos.config.file-extension}
# nacos-config-client-dev.yaml
3)启动类添加注解@EnableDiscoveryClient
/**
* ConfigNacosMain3344
*
* @author lcry
* @date 2020/03/16 17:49
*/
@SpringBootApplication
@EnableDiscoveryClient
public class ConfigNacosMain3377 {
public static void main(String[] args) {
SpringApplication.run(ConfigNacosMain3377.class, args);
}
}
4)业务类添加注解@RefreshScope
/**
* NacosClientController
*
* @author lcry
*/
@RestController
@RefreshScope //支持Nacos的动态刷新功能。
public class NacosClientController {
@Value("${config.info}") //通过nacos配置中心获取文件
private String configInfo;
@GetMapping("/config/info")
public String getConfigInfo() {
return configInfo;
}
}
对应项目:cloudalibaba-config-nacos-client3377
3、使用Sentiel限流
1)导入依赖
<!--SpringCloud ailibaba nacos -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--SpringCloud ailibaba sentinel-datasource-nacos 后续做持久化用到-->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
<!--SpringCloud ailibaba sentinel -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<!--openfeign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
2)application.yml配置示例
#测试端口
server:
port: 8401
spring:
application:
name: cloudalibaba-sentinel-service
cloud:
nacos:
discovery:
server-addr: localhost:8848 #Nacos服务注册中心地址
sentinel:
transport:
dashboard: localhost:8080 #配置Sentinel dashboard地址
port: 8719 #随机找一个地址,如果冲突就往后+1
#持久化配置
# datasource:
# ds1:
# nacos:
# server-addr: localhost:8848
# dataId: cloudalibaba-sentinel-service
# groupId: DEFAULT_GROUP
# data-type: json
# rule-type: flow
management:
endpoints:
web:
exposure:
include: '*'
feign:
sentinel:
enabled: true # 激活Sentinel对Feign的支持
3)接口先请求一次,然后在sentinel控制台进行相应限流模式操作
4)自定义限流异常页面等,使用注解@SentinelResource
@GetMapping("/testHotKey")
@SentinelResource(value = "testHotKey", blockHandler = "deal_testHotKey")
public String testHotKey(@RequestParam(value = "p1", required = false) String p1,
@RequestParam(value = "p2", required = false) String p2) {
//int age = 10/0;
return "------testHotKey";
}
public String deal_testHotKey(String p1, String p2, BlockException exception) {
//sentinel系统默认的提示:Blocked by Sentinel (flow limiting)
return "------deal_testHotKey,o(╥﹏╥)o";
}
@GetMapping("/rateLimit/byResource")
@SentinelResource(value = "byResource", blockHandler = "handleException")
public CommonResult byResource() {
return new CommonResult(200, "按资源名称限流测试OK", new Payment(2020L, "serial001"));
}
public CommonResult handleException(BlockException exception) {
return new CommonResult(444, exception.getClass().getCanonicalName() + "\t 服务不可用");
}
@GetMapping("/rateLimit/byUrl")
@SentinelResource(value = "byUrl")
public CommonResult byUrl() {
return new CommonResult(200, "按url限流测试OK", new Payment(2020L, "serial002"));
}
@GetMapping("/rateLimit/customerBlockHandler")
@SentinelResource(value = "customerBlockHandler", //资源名称
blockHandlerClass = CustomerBlockHandler.class, //异常自定义
blockHandler = "handlerException1") //调用自定义类某个方法
public CommonResult customerBlockHandler() {
return new CommonResult(200, "按客戶自定义", new Payment(2020L, "serial003"));
}
/**
* @author lcry
* @date 2020/03/17 14:51
* 用户自定义处理异常类
*/
public class CustomerBlockHandler {
public static CommonResult handlerException1(BlockException exception) {
return new CommonResult(4444, "按客戶自定义,global handlerException----1");
}
public static CommonResult handlerException2(BlockException exception) {
return new CommonResult(4444, "按客戶自定义,global handlerException----2");
}
}
对应项目:cloudalibaba-sentinel-service8401
4、使用Sentinel服务熔断
1)导入依赖
<!-- 服务消费方,整合rabbin和feign,sentinel限流熔断等-->
<!--SpringCloud openfeign -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!--SpringCloud ailibaba sentinel-datasource-nacos做持久化用到-->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
<!--SpringCloud ailibaba nacos -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--SpringCloud ailibaba sentinel -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
2)启动类添加启用Openfeign注解@EnableFeignClients
@EnableDiscoveryClient
@SpringBootApplication
@EnableFeignClients //整合feign
public class NacosOrderMain84 {
public static void main(String[] args) {
SpringApplication.run(NacosOrderMain84.class, args);
}
}
3)熔断和降级示例业务代码
/**
* CircleBreakerController
*
* @author lcry
* 使用示例:
*/
@RestController
@Slf4j
public class CircleBreakerController {
public static final String SERVICE_URL = "http://nacos-payment-provider";
// 整个rabbin - restTemplate
@Resource
private RestTemplate restTemplate;
// 整合sentinel
@RequestMapping("/consumer/fallback/{id}")
//@SentinelResource(value = "fallback") //没有配置
//@SentinelResource(value = "fallback",fallback = "handlerFallback") //fallback只负责业务异常
//@SentinelResource(value = "fallback",blockHandler = "blockHandler") //blockHandler只负责sentinel控制台配置违规
// @SentinelResource(value = "fallback",fallback = "handlerFallback",blockHandler = "blockHandler", //分开自定义
// exceptionsToIgnore = {IllegalArgumentException.class}//排除异常不限流
// )
@SentinelResource(value = "fallback", //,fallback = "handlerFallback",
blockHandlerClass = CustomerBlockHandler.class, //异常自定义
blockHandler = "handlerException1" //分开自定义
)
public CommonResult<Payment> fallback(@PathVariable Long id) {
CommonResult<Payment> result = restTemplate.getForObject(SERVICE_URL + "/paymentSQL/" + id, CommonResult.class, id);
if (id == 4) {
throw new IllegalArgumentException("IllegalArgumentException,非法参数异常....");
} else if (result.getData() == null) {
throw new NullPointerException("NullPointerException,该ID没有对应记录,空指针异常");
}
return result;
}
//本例是fallback
public CommonResult handlerFallback(@PathVariable Long id, Throwable e) {
Payment payment = new Payment(id, "null");
return new CommonResult<>(444, "兜底异常handlerFallback,exception内容 " + e.getMessage(), payment);
}
//本例是blockHandler
public CommonResult blockHandler(@PathVariable Long id, BlockException blockException) {
Payment payment = new Payment(id, "null");
return new CommonResult<>(445, "blockHandler-sentinel限流,无此流水: blockException " + blockException.getMessage(), payment);
}
//OpenFeign调用
@Resource
private PaymentService paymentService;
@GetMapping(value = "/consumer/paymentSQL/{id}")
public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id) {
return paymentService.paymentSQL(id);
}
/*
测试自定义限流
*/
@GetMapping(value = "/consumer/paymentSQL")
@SentinelResource(value = "customerBlockHandler", //资源名称
blockHandlerClass = CustomerBlockHandler.class, //异常自定义
blockHandler = "handlerException1") //调用自定义类某个方法
public CommonResult<Payment> paymentSQL2() {
return paymentService.paymentSQL(1L);
}
}
对应项目:cloudalibaba-consumer-nacos-order84
5、使用分布式事务Seata
1)引入依赖
<!--nacos-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--seata-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
<!-- 排除自带的,我们使用0.9.0-->
<exclusions>
<exclusion>
<artifactId>seata-all</artifactId>
<groupId>io.seata</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-all</artifactId>
<version>0.9.0</version>
</dependency>
<!--feign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
2)application.yml配置示例
#微服务端口
server:
port: 12001
spring:
application:
name: seata-order-service
cloud:
alibaba:
seata:
#自定义事务组名称需要与seata-server中的对应
tx-service-group: fsp_tx_group
nacos:
discovery:
server-addr: localhost:8848
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/seata_order?useUnicode=true&characterEncoding=utf-8&useSSL=false
username: root
password: lcry
#禁用feign
feign:
hystrix:
enabled: false
#OpenFeign的超时控制配置:默认只等待1秒,超时则报错
#设置Feign客户端超时时间(OpenFeign默认支持Ribbon)
ribbon:
#指的是建立连接所用的时间,适用于网络状况正常的情况下,两端连接所用的时间
ReadTimeout: 5000
#指的是建立连接后从服务器读取到可用资源所用的时间
connectTimeout: 5000
#日志级别
logging:
level:
io:
seata: info
#mybatis映射
mybatis:
mapperLocations: classpath:mapper/*.xml
3)配置file.conf和register.conf,可以直接从Seata上直接拷贝到resources目录下做部分改动
4)业务代码添加注解@GlobalTransactional开启全局事务
/**
* 创建订单->调用库存服务扣减库存->调用账户服务扣减账户余额->修改订单状态
* 简单说:下订单->扣库存->减余额->改状态
*/
@Override
@GlobalTransactional(name = "tx-create-order", rollbackFor = Exception.class)
public void create(Order order) {
log.info("----->开始新建订单");
//1 新建订单
orderDao.create(order);
//2 扣减库存
log.info("----->订单微服务开始调用库存,做扣减Count");
storageService.decrease(order.getProductId(), order.getCount());
log.info("----->订单微服务开始调用库存,做扣减end");
//3 扣减账户
log.info("----->订单微服务开始调用账户,做扣减Money");
accountService.decrease(order.getUserId(), order.getMoney());
log.info("----->订单微服务开始调用账户,做扣减end");
//4 修改订单状态,从零到1,1代表已经完成
log.info("----->修改订单状态开始");
orderDao.update(order.getUserId(), 0);
log.info("----->修改订单状态结束");
log.info("----->下订单结束了,O(∩_∩)O哈哈~");
}
对应项目:cloudalibaba-seata-order-service12001、cloudalibaba-seata-storage-service12002、cloudalibaba-seata-account-service12003
完结~