what:是什么
why:为什么使用
where:在哪里使用
when:什么时候使用
how:如何使用
SpringCloud是一系列框架组合,为了避免与框架版本产生混淆,采用全新的命名方式
版本查看地址:https://spring.io/projects/spring-cloud
我们选用的版本是:Spring Cloud Hoxton.SR5 + Spring boot 2.3.7.RELEASE
概述
准备数据
-- 创建数据库
drop database if exists springcloud;
create database springcloud character set utf8 collate utf8_general_ci;
-- 使用springcloud数据库
use springcloud;
-- ----------------------------
-- table structure for tb_user
-- ----------------------------
create table `tb_user` (
`id` int(11) not null auto_increment,
`username` varchar(100) default null comment '用户名',
`password` varchar(100) default null comment '密码',
`name` varchar(100) default null comment '姓名',
`age` int(11) default null comment '年龄',
`sex` int(11) default null comment '性别,1男,2女',
`birthday` date default null comment '出生日期',
`created` date default null comment '创建时间',
`updated` date default null comment '更新时间',
`note` varchar(1000) default null comment '备注',
primary key (`id`)
) engine=innodb auto_increment=2 default charset=utf8 comment='用户信息表';
-- ----------------------------
-- records of tb_user
-- ----------------------------
insert into `tb_user` values ('1', 'zhangsan', '123456', '张三', '13', '1', '2006-08-01', '2019-05-16', '2019-05-16', '张三');
insert into `tb_user` values ('2', 'lisi', '123456', '李四', '13', '1', '2006-08-01', '2019-05-16', '2019-05-16', '李四');
实现步骤
配置信息:application.yml
# 端口
server.port: 9091
# 数据库连接配置信息
spring.datasource.driver-class-name: com.mysql.cj.jdbc.Driver
spring.datasource.url: jdbc:mysql://127.0.0.1:3306/springcloud?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
spring.datasource.password: root
spring.datasource.username: root
User
package com.lxgzhw.pojo;
import lombok.Data;
import java.util.Date;
@Data
public class User {
private Integer id;//主键id
private String username;//用户名
private String password;//密码
private String name;//姓名
private Integer age;//年龄
private Integer sex;//性别 1男性,2女性
private Date birthday; //出生日期
private Date created; //创建时间
private Date updated; //更新时间
private String note;//备注
//getter setter
}
UserService
package com.lxgzhw.service;
import com.lxgzhw.pojo.User;
public interface UserService {
User findById(Integer id);
}
UserServiceImpl
package com.lxgzhw.service.impl;
import com.lxgzhw.mapper.UserMapper;
import com.lxgzhw.pojo.User;
import com.lxgzhw.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Override
public User findById(Integer id) {
return userMapper.findById(id);
}
}
UserMapper
package com.lxgzhw.mapper;
import com.lxgzhw.pojo.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import org.springframework.stereotype.Repository;
@Mapper
@Repository
public interface UserMapper {
@Select("select *from tb_user where id = #{id}")
User findById(Integer id);
}
UserController
package com.lxgzhw.controller;
import com.lxgzhw.pojo.User;
import com.lxgzhw.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/findById")
public User findById(@RequestParam("id") Integer id) {
return userService.findById(id);
}
}
启动服务器测试:http://localhost:9091/user/findById?id=1
启动类
package com.lxgzhw;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
@SpringBootApplication
public class ConsumerServiceApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerServiceApplication.class, args);
}
// 相当于application.xml中的一个bean标签
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
User实体类
package com.lxgzhw.pojo;
import lombok.Data;
import java.util.Date;
@Data
public class User {
private Integer id;//主键id
private String username;//用户名
private String password;//密码
private String name;//姓名
private Integer age;//年龄
private Integer sex;//性别 1男性,2女性
private Date birthday; //出生日期
private Date created; //创建时间
private Date updated; //更新时间
private String note;//备注
//getter setter
}
ConsumerController类
package com.lxgzhw.controller;
import com.lxgzhw.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;
@RestController
@RequestMapping("/consumer")
public class ConsumerController {
@Autowired
private RestTemplate restTemplate;
/**
* 模拟发送一次请求
*/
@GetMapping("/{id}")
public User consumerSendRequest(@PathVariable("id") Integer id){
String url = "http://localhost:9091/user/findById?id="+id;
User user = restTemplate.getForObject(url, User.class);
return user;
}
}
启动服务器测试:http://localhost:8080/consumer/1
存在的问题:
其实上面说的部分问题,概括一下就是微服务架构必然面临的一些问题。
what:是什么
why:为什么要使用注册中心
where:在哪里使用
when:什么时候使用
how:如何使用
基本步骤
创建注册中心模块
基本配置:application.yml
# 端口
server.port: 10086
# 应用名称,会在Eureka中作为服务的id标识(serviceId)
spring.application.name: eureka-server
# EurekaServer的地址,现在是自己的地址,如果是集群,需要写其它Server的地址。
eureka.client.service-url.defaultZone: http://127.0.0.1:10086/eureka
# 是否抓取注册列表
eureka.client.fetch-registry: false
# 是否注册服务中心Eureka
eureka.client.register-with-eureka: false
启动类
package com.lxgzhw;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@SpringBootApplication
@EnableEurekaServer //开启EurekaServer端的自动配置
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}
在pom.xml中添加以下插件解决点击install时失败的问题
<plugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-resources-pluginartifactId>
<version>2.4.3version>
plugin>
启动服务器测试:http://127.0.0.1:10086/
服务提供者-注册到eureka
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
dependency>
dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-dependenciesartifactId>
<version>Hoxton.SR5version>
<type>pomtype>
<scope>importscope>
dependency>
dependencies>
dependencyManagement>
@EnableDiscoveryClient
package com.lxgzhw;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@EnableDiscoveryClient // 将服务交给Eureka注册中心管理
public class ProviderServerApplication {
public static void main(String[] args) {
SpringApplication.run(ProviderServerApplication.class, args);
}
}
# 端口
server.port: 9091
# DB 配置
spring.datasource.driver-class-name: com.mysql.cj.jdbc.Driver
spring.datasource.url: jdbc:mysql://127.0.0.1:3306/springcloud?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
spring.datasource.password: root
spring.datasource.username: root
# 应用名称
spring.application.name: user-service
# 注册中心地址
eureka.client.service-url.defaultZone: http://127.0.0.1:10086/eureka
服务消费者-注册到eureka
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
dependency>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-dependenciesartifactId>
<version>Hoxton.SR5version>
<type>pomtype>
<scope>importscope>
dependency>
dependencies>
dependencyManagement>
@EnableDiscoveryClient
package com.lxgzhw;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
@SpringBootApplication
@EnableDiscoveryClient
public class ConsumerServerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerServerApplication.class, args);
}
// 相当于application.xml中的一个bean标签
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
# 端口
server.port: 8080
# 应用名称
spring.application.name: consumer-server
# 注册中心地址
eureka.client.service-url.defaultZone: http://127.0.0.1:10086/eureka
服务注册(register
服务续约(renew)
eureka.instance.lease-renewal-interval-in-seconds
发送一次心跳来进行服务续约。配置消费者或服务提供者
# 租约续约间隔时间,默认30秒
eureka.instance.lease-renewal-interval-in-seconds: 30
获取服务列表(get registry)
配置消费者或服务提供者
# 每隔多久获取服务中心列表,(只读备份)
eureka.client.registry-fetch-interval-seconds: 30
服务调用
服务下线(cancel)
失效剔除(evict)
eureka.instance.lease-expiration-duration-in-seconds
)的服务剔除。配置消费者或服务提供者
# 租约到期,服务时效时间,默认值60秒
eureka.instance.lease-expiration-duration-in-seconds: 90
自我保护
配置到注册中心
#向Eureka服务中心集群注册服务
eureka.server.enable-self-preservation: false # 关闭自我保护模式(默认值是打开)
修改消费者服务的ConsumerController类
package com.lxgzhw.controller;
import com.lxgzhw.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;
import java.util.List;
@RestController
@RequestMapping("/consumer")
public class ConsumerController {
@Autowired
private RestTemplate restTemplate;
/**
* 模拟发送一次请求
*/
//@GetMapping("/{id}")
public User consumerSendRequest1(@PathVariable("id") Integer id) {
String url = "http://localhost:9091/user/" + id;
User user = restTemplate.getForObject(url, User.class);
return user;
}
@Autowired
private DiscoveryClient discoveryClient; // 服务发现对象
@GetMapping("/{id}")
public User consumerSendRequest(@PathVariable("id") Integer id) {
// 动态获取URL 和 端口号
List<ServiceInstance> instances = discoveryClient.getInstances("user-service");
// 获取集合的第一个元素
ServiceInstance serviceInstance = instances.get(0);
// 获取服务的host地址
String host = serviceInstance.getHost();
// 获取服务的port端口
int port = serviceInstance.getPort();
// 动态拼接访问地址
String url = "http://" + host + ":" + port + "/user/" + id;
// 发送请求的方式不变
User user = restTemplate.getForObject(url, User.class);
return user;
}
}
重启消费者服务器测试:http://localhost:8080/consumer/1
问题
@GetMapping(value = "/{id}", produces = "application/json; charset=UTF-8")
what:是什么
why:为什么使用
where:在哪里使用
when:什么时候使用
how:如何使用
编辑配置
复制几个提供者服务
修改配置文件:动态注入端口号
# 端口
server.port: ${port:9091}
编辑配置里面动态传入参数,分别为
-Dport=9091
-Dport=9092
-Dport=9093
从configure里面找到springboot
基本步骤
具体修改
package com.lxgzhw;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
@SpringBootApplication
@EnableDiscoveryClient
public class ConsumerServerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerServerApplication.class, args);
}
// 相当于application.xml中的一个bean标签
@Bean
@LoadBalanced //开启消费者负载均衡
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
package com.lxgzhw.controller;
import com.lxgzhw.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;
import java.util.List;
@RestController
@RequestMapping("/consumer")
public class ConsumerController {
@Autowired
private RestTemplate restTemplate;
/**
* 模拟发送一次请求
*/
//@GetMapping("/{id}")
public User consumerSendRequest1(@PathVariable("id") Integer id) {
String url = "http://localhost:9091/user/" + id;
User user = restTemplate.getForObject(url, User.class);
return user;
}
@Autowired
private DiscoveryClient discoveryClient; // 服务发现对象
//@GetMapping(value = "/{id}", produces = "application/json; charset=UTF-8")
public User consumerSendRequest2(@PathVariable("id") Integer id) {
// 动态获取URL 和 端口号
List<ServiceInstance> instances = discoveryClient.getInstances("user-service");
// 获取集合的第一个元素
ServiceInstance serviceInstance = instances.get(0);
// 获取服务的host地址
String host = serviceInstance.getHost();
// 获取服务的port端口
int port = serviceInstance.getPort();
// 动态拼接访问地址
String url = "http://" + host + ":" + port + "/user/" + id;
// 发送请求的方式不变
User user = restTemplate.getForObject(url, User.class);
return user;
}
/**
* 负载均衡的调用方式
* @param id 请求id
* @return 用户对象json字符串
*/
@GetMapping(value = "/{id}", produces = "application/json; charset=UTF-8")
public User consumerSendRequest(@PathVariable("id") Integer id) {
// 使用负载均衡的方式访问: 会从集群中按照算法指定的方式返回一个服务器的数据
String url = "http://user-service/user/" + id;
// 发送请求的方式不变
User user = restTemplate.getForObject(url, User.class);
return user;
}
}
what:是什么
why:为什么使用
where:在哪里使用
when:什么时候使用
how:怎么使用
什么是雪崩效应?
引入熔断的依赖坐标:consumer_service中加入依赖
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-hystrixartifactId>
dependency>
开启熔断的注解:修改消费者服务的启动类
package com.lxgzhw;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.SpringCloudApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
//注解简化写法:微服务中,注解往往引入多个,简化注解可以使用组合注解。@SpringCloudApplication =等同于@SpringBootApplication+@EnableDiscoveryClient+@EnableCircuitBreaker
@SpringCloudApplication
//@SpringBootApplication
//@EnableDiscoveryClient//开启服务发现
//@EnableCircuitBreaker//开启熔断
public class ConsumerServerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerServerApplication.class, args);
}
// 相当于application.xml中的一个bean标签
@Bean
@LoadBalanced //开启消费者负载均衡
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
编写服务降级处理方法:使用@HystrixCommand定义fallback方法。修改消费者服务的ConsumerController类
package com.lxgzhw.controller;
import com.lxgzhw.pojo.User;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;
import java.util.List;
@RestController
@RequestMapping("/consumer")
public class ConsumerController {
@Autowired
private RestTemplate restTemplate;
/**
* 模拟发送一次请求
*/
//@GetMapping("/{id}")
public User consumerSendRequest1(@PathVariable("id") Integer id) {
String url = "http://localhost:9091/user/" + id;
User user = restTemplate.getForObject(url, User.class);
return user;
}
@Autowired
private DiscoveryClient discoveryClient; // 服务发现对象
//@GetMapping(value = "/{id}", produces = "application/json; charset=UTF-8")
public User consumerSendRequest2(@PathVariable("id") Integer id) {
// 动态获取URL 和 端口号
List<ServiceInstance> instances = discoveryClient.getInstances("user-service");
// 获取集合的第一个元素
ServiceInstance serviceInstance = instances.get(0);
// 获取服务的host地址
String host = serviceInstance.getHost();
// 获取服务的port端口
int port = serviceInstance.getPort();
// 动态拼接访问地址
String url = "http://" + host + ":" + port + "/user/" + id;
// 发送请求的方式不变
User user = restTemplate.getForObject(url, User.class);
return user;
}
/**
* 负载均衡的调用方式
* @param id 请求id
* @return 用户对象json字符串
*/
//@GetMapping(value = "/{id}", produces = "application/json; charset=UTF-8")
public User consumerSendRequest(@PathVariable("id") Integer id) {
// 使用负载均衡的方式访问: 会从集群中按照算法指定的方式返回一个服务器的数据
String url = "http://user-service/user/" + id;
// 发送请求的方式不变
User user = restTemplate.getForObject(url, User.class);
return user;
}
/**
* 负债均衡+服务熔断的写法
* @param id 用户id
* @return 用户对象
*/
@GetMapping(value = "/{id}", produces = "application/json; charset=UTF-8")
@HystrixCommand(fallbackMethod ="queryByIdFallback")
public String queryById(@PathVariable Long id){
String url = String.format("http://user-service/user/%d", id);
return restTemplate.getForObject(url,String.class);
}
/**
* 服务熔断处理方法
* @param id 用户id,熔断方法的参数必须与请求方法的参数保持一致
* @return 错误处理信息
*/
public String queryByIdFallback(Long id){
return "对不起,网络太拥挤了!";
}
}
配置熔断策略:常见熔断策略配置。修改消费者服务的配置文件
# 配置熔断策略:
# 强制打开熔断器 默认false关闭的。测试配置是否生效
hystrix.command.default.circuitBreaker.forceOpen: false
# 触发熔断错误比例阈值,默认值50%
hystrix.command.default.circuitBreaker.errorThresholdPercentage: 20
# 熔断后休眠时长,默认值5秒
hystrix.command.default.circuitBreaker.sleepWindowInMilliseconds: 60000
# 熔断触发最小请求次数,默认值是20
hystrix.command.default.circuitBreaker.requestVolumeThreshold: 5
# 熔断超时设置,默认为1秒
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds: 2000
模拟异常代码:修改ConsumerController中的请求方法
/**
* 负债均衡+服务熔断的写法
* @param id 用户id
* @return 用户对象
*/
@GetMapping(value = "/{id}", produces = "application/json; charset=UTF-8")
@HystrixCommand(fallbackMethod ="queryByIdFallback")
public String queryById(@PathVariable Long id){
//如果参数为1抛出异常,否则 执行REST请求返回user对象
if (id == 1){
throw new RuntimeException("too busy!!!");
}
String url = String.format("http://user-service/user/%d", id);
return restTemplate.getForObject(url,String.class);
}
重启消费者服务,访问测试:http://localhost:8080/consumer/1
实现步骤
@DefaultProperties(defaultFallback = "defaultFallback")
注解@HystrixCommand
注解public String defaultFallback(){}
实例代码:修改消费者服务的ConsumerController类
package com.lxgzhw.controller;
import com.lxgzhw.pojo.User;
import com.netflix.hystrix.contrib.javanica.annotation.DefaultProperties;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;
import java.util.List;
@RestController
@RequestMapping("/consumer")
@DefaultProperties(defaultFallback = "defaultFallback")//开启默认的FallBack,统一失败降级方法(兜底)
public class ConsumerController {
@Autowired
private RestTemplate restTemplate;
/**
* 模拟发送一次请求
*/
//@GetMapping("/{id}")
public User consumerSendRequest1(@PathVariable("id") Integer id) {
String url = "http://localhost:9091/user/" + id;
User user = restTemplate.getForObject(url, User.class);
return user;
}
@Autowired
private DiscoveryClient discoveryClient; // 服务发现对象
//@GetMapping(value = "/{id}", produces = "application/json; charset=UTF-8")
public User consumerSendRequest2(@PathVariable("id") Integer id) {
// 动态获取URL 和 端口号
List<ServiceInstance> instances = discoveryClient.getInstances("user-service");
// 获取集合的第一个元素
ServiceInstance serviceInstance = instances.get(0);
// 获取服务的host地址
String host = serviceInstance.getHost();
// 获取服务的port端口
int port = serviceInstance.getPort();
// 动态拼接访问地址
String url = "http://" + host + ":" + port + "/user/" + id;
// 发送请求的方式不变
User user = restTemplate.getForObject(url, User.class);
return user;
}
/**
* 负载均衡的调用方式
* @param id 请求id
* @return 用户对象json字符串
*/
//@GetMapping(value = "/{id}", produces = "application/json; charset=UTF-8")
public User consumerSendRequest(@PathVariable("id") Integer id) {
// 使用负载均衡的方式访问: 会从集群中按照算法指定的方式返回一个服务器的数据
String url = "http://user-service/user/" + id;
// 发送请求的方式不变
User user = restTemplate.getForObject(url, User.class);
return user;
}
/**
* 负债均衡+服务熔断的写法
* @param id 用户id
* @return 用户对象
*/
@GetMapping(value = "/{id}", produces = "application/json; charset=UTF-8")
//@HystrixCommand(fallbackMethod ="queryByIdFallback")
@HystrixCommand //使用全局默认的降级方法
public String queryById(@PathVariable Long id){
//如果参数为1抛出异常,否则 执行REST请求返回user对象
if (id == 1){
throw new RuntimeException("too busy!!!");
}
String url = String.format("http://user-service/user/%d", id);
return restTemplate.getForObject(url,String.class);
}
/**
* 服务熔断处理方法
* @param id 用户id,熔断方法的参数必须与请求方法的参数保持一致
* @return 错误处理信息
*/
public String queryByIdFallback(Long id){
return "对不起,网络太拥挤了!";
}
/**
* 默认降级方法,不需要任何参数
*/
public String defaultFallback(){
return "默认提示:对不起,网络太拥挤了!";
}
}
重启消费者服务,访问:http://localhost:8080/consumer/1
源码下载:https://download.csdn.net/download/qq_37703224/13738563
想要学习Python或者Java的同学可以加我微信18010070052,第一次添加我微信可以免费获得本文对应源码哦