一、目的
实验目的:
掌握Spring Cloud组件模型及编程框架
实验要求:
独立完成实验操作,并撰写实验报告
实验内容:
1. 搭建Spring Cloud框架
使用原生Spring Cloud或Spring Cloud Alibaba搭建一个简单的Spring Cloud框架,组件尽可能包含五个核心组件:注册中心、负载均衡、熔断降级、路由管关、配置中心。
2. 产品列表
在上述Spring Cloud框架中,实现产品列表的功能,要求合理设计微服务、组件,综合考虑独立性、安全性、可扩展性等因素。
二、实验内容与设计思想
2.1 搭建Spring Cloud框架
搭建父项目 搭建eureka-server注册中心 搭建提供者服务 搭建消费者服务 实现服务之间的调用
2.2 产品列表
建立产品数据库和表 添加product表的controller,service,serviceImpl,domain,mapper到服务提供者中 在服务消费者中对产品列表的提供者进行调用
三、实验使用环境
平台:win10
软件:idea
四、实验步骤和调试过程
4.1 搭建Spring Cloud框架
4.1.1 实验步骤
搭建父项目 搭建eureka-server注册中心
添加依赖:
springwebeureka server
启动类加上注解:
@EnableEurekaServer
配置(设置端口为8090)
server:
port: 8090
spring:
application:
#应用名称(在注册中显示的)
name: eureka-server
eureka:
client:
#此客户端是否获取eureka服务器注册表上的注册信息,默认为true
fetch-registry: false
#实例是否在eureka服务器上注册自己的信息以供其他服务发现,默认为true,即自己注册自己。
register-with-eureka: true
#与Eureka注册服务中心的通信zone和url地址
serviceUrl:
#http://localhost:8090/eureka/eureka
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka
#服务注册中心实例的主机名
instance:
hostname: 127.0.0.1
prefer-ip-address: true
instance-id: 127.0.0.1:8090
server:
#设为false,关闭自我保护,即Eureka server在云心光器件会去统计心跳失败比例在15分钟之内是否低于85%,如果低于85%,EurekaServer
#会将这些事例保护起来,让这些事例不会过期,但是在保护器内如果刚哈这个服务提供者非正常下线了,此时服务消费者会拿到一个无效的服务
#实例,此时调用会失败,对于这个问题需要服务消费者端有一些容错机制,如重试、断路器等;
enable-self-preservation: false
#扫描失效服务的间隔时间(单位是毫秒,摩恩是60*1000),即60s
eviction-interval-timer-in-ms: 10000
搭建提供者服务
添加依赖:
springwebeureka discovery client
启动类加上注解:
@EnableEurekaClient
@SpringBootApplication
@EnableHystrix
配置(端口为8091和8092)
spring:
application:
name: cloud-provider
server:
port: 8091
eureka:
client:
#此客户端是否获取eureka服务器注册表上的注册信息,默认为true
fetch-registry: false
#实例是否在eureka服务器上注册自己的信息以供其他服务发现,默认为true,即自己注册自己。
register-with-eureka: true
service-url:
#defaultZone 这个是不会提示的,此处需要自己写
#实际上属性应该是service-url,这个属性是个map(key-value)格式;当key是defaultZone的时候才能被解析;所以这里没有提示,
#但是自己还需要写一个defaultZone;
defaultZone: http://localhost:8090/eureka
#服务注册中心实例的主机名
instance:
hostname: 127.0.0.1
prefer-ip-address: true
instance-id: 127.0.0.1:8091
建立一个controller进行测试,并配置了熔断设置:
@RestController
@RequestMapping("/helloProvider")
public class HelloController {
@Autowired
private HelloService helloService;
@GetMapping("/getHello")
public String getHello(){
return helloService.getHello();
}
@GetMapping("/hystrix")
@HystrixCommand(fallbackMethod = "fallBack")
public String hystrix() throws InterruptedException
{
int i = 1/0;
return "hystrix";
}
public String fallBack(){
return "发生熔断 --> fallBack()方法";
}
}
搭建消费者服务 添加依赖:
springwebeureka discovery clientopenfiegn
启动类加上注解:
@SpringBootApplication @EnableEurekaClient @EnableFeignClients
配置(端口为8095):
server:
#定义端口号
port: 8095
spring:
application:
#定义应用名称,即服务名称
name: cloud-client
eureka:
client:
service-url:
defaultZone: http://localhost:8090/eureka
#服务注册中心实例的主机名
instance:
hostname: 127.0.0.1
prefer-ip-address: true
instance-id: 127.0.0.1:8095
feign:
circuitbreaker:
enabled: true
client:
config:
pay-service: # 对服务提供者(优先级高):填对应服务提供者名称,
# 对所有提供者(优先级低):固定"default"
connectTimeout: 3000 # 连接超时时间单位ms
readTimeout: 8000 # 读取超时时间单位ms
ribbon:
ReadTimeout: 60000 #ribbon连接超时
ConnectTimeout: 60000 #ribbon读取超时
建立controller进行测试:
public class HelloController
{
@Autowired
private HelloService helloService;
@GetMapping
public String getHello()
{
return helloService.getProduct();
}
}
service接口对提供者进行关联:
@FeignClient(name = "cloud-provider",path ="/helloProvider" , fallbackFactory = MyFallBack.class)
@Component
public interface HelloService {
@RequestMapping(value = "getHello")
String getProduct();
@RequestMapping(value = "hystrix")
String hystrix();
}
并配置降级设置:
@Component
public class MyFallBack implements FallbackFactory
{
@Override
public HelloService create(Throwable cause)
{
return new HelloService()
{
@Override
public String getProduct()
{
return "降级发生";
}
@Override
public String hystrix()
{
return "降级发生";
}
};
}
}
geteway 添加依赖:
gateway 配置(端口为8301):
# 路由Routing配置
server:
port: 8301
# 参数
service-url:
user-service: http://localhost:8095/
# Gateway配置
spring:
cloud:
gateway:
routes:
- id: path_route # 路由唯一标识
uri: ${service-url.user-service} #路由指向的目的URL或服务名,客户端请求最终被转发到的微服务
# uri: lb://SPRINGCLOUD-SERVICE
predicates:
- After=2023
- Path=/** # 断言:以/test/开头的所有请求都负载到前述uri指定的服务
#logging:
# charset:
# console: utf-8
# file: utf-8
4.2.2 测试
全部服务开启后:
测试负载均衡:
测试gateway:
测试熔断(在服务提供者的serviceImpl中添加一个错误):
测试降级(将服务提供者断开后):
4.2 产品列表
4.2.1 建库建表
建立产品表,字段有id,product_name,product_information。
DROP TABLE IF EXISTS `product`;
CREATE TABLE `product` (
`id` int NOT NULL,
`product_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
`product_information` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
4.2.2 实验步骤
添加product表的controller,service,serviceImpl,domain,mapper到服务提供者中
public class ProductController {
/**
* 服务对象
*/
@Resource
private ProductService productService;
/**
* 全查询
*
* @param product 筛选条件
* @return 查询结果
*/
@GetMapping
public R queryAll(Product product) {
System.out.println("查询");
return this.productService.queryAll(product);
}
/**
* 分页查询
*
* @param product 筛选条件
* @param pageSize 每页多少航
* @param page 第几页
* @return 查询结果
*/
@GetMapping("/page")
public R queryAllPage(Product product, Integer pageSize, Integer page) {
return this.productService.queryAllPage(product,pageSize,page);
}
/**
* 模糊查找
*
* @param searchText 查找关键字
* @return 查询结果
*/
@GetMapping("/search/{searchText}")
public R queryBySearchText(@PathVariable("searchText") String searchText) {
return this.productService.queryBySearchText(searchText);
}
/**
* 通过主键查询单条数据
*
* @param id 主键
* @return 单条数据
*/
@GetMapping("{id}")
public R queryById(@PathVariable("id") Integer id) {
return this.productService.queryById(id);
}
/**
* 统计总行数
*
* @param product 筛选条件
* @return 查询结果
*/
@GetMapping("/count")
public R getCount(Product product) {
return this.productService.getCount(product);
}
/**
* 新增数据
*
* @param product 实体
* @return 新增结果
*/
@PostMapping
public R add(@RequestBody Product product) {
return this.productService.insert(product);
}
/**
* 更新数据
*
* @param product 实体
* @return 更新结果
*/
@PutMapping
public R update(@RequestBody Product product) {
return this.productService.update(product);
}
/**
* 删除数据
*
* @param id 主键
* @return 删除是否成功
*/
@DeleteMapping
public R deleteById(Integer id) {
return this.productService.deleteById(id);
}
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Product implements Serializable {
private static final long serialVersionUID = 758948157281713094L;
private Integer id;
private String productName;
private String productInformation;
}
@Mapper
public interface ProductMapper {
/**
* 通过ID查询单条数据
*
* @param id 主键
* @return 实例对象
*/
Product queryById(Integer id);
/**
* 全查询
*
* @param product 查询条件
* @return 对象列表
*/
List
/**
* 分页查询
*
* @param product 查询条件
* @param pageSize 每页多少航
* @param page 第几页
* @return 对象列表
*/
List
@Param("pageSize") Integer pageSize ,@Param("page") Integer page);
/**
* 模糊查找
*
* @param searchText 查找关键字
* @return 查询结果
*/
List
/**
* 统计总行数
*
* @param product 查询条件
* @return 总行数
*/
long count(Product product);
/**
* 新增数据
*
* @param product 实例对象
* @return 影响行数
*/
int insert(Product product);
/**
* 批量新增数据(MyBatis原生foreach方法)
*
* @param entities List
* @return 影响行数
*/
int insertBatch(@Param("entities") List
/**
* 批量新增或按主键更新数据(MyBatis原生foreach方法)
*
* @param entities List
* @return 影响行数
* @throws org.springframework.jdbc.BadSqlGrammarException 入参是空List的时候会抛SQL语句错误的异常,请自行校验入参
*/
int insertOrUpdateBatch(@Param("entities") List
/**
* 修改数据
*
* @param product 实例对象
* @return 影响行数
*/
int update(Product product);
/**
* 通过主键删除数据
*
* @param id 主键
* @return 影响行数
*/
int deleteById(Integer id);
@Service("productService")
public class ProductServiceImpl implements ProductService {
@Resource
private ProductMapper productMapper;
/**
* 通过ID查询单条数据
*
* @param id 主键
* @return 实例对象
*/
@Override
public R queryById(Integer id) {
return R.ok().setData(this.productMapper.queryById(id));
}
/**
* 分页查询
*
* @param product 筛选条件
* @param pageSize 每页多少航
* @param page 第几页
* @return 查询结果
*/
@Override
public R queryAllPage(Product product,Integer pageSize, Integer page) {
return R.ok().setData(this.productMapper.queryAllPage(product,pageSize,page));
}
/**
* 全查询
*
* @param product 筛选条件
* @return 查询结果
*/
@Override
public R queryAll(Product product) {
return R.ok().setData(this.productMapper.queryAll(product));
}
/**
* 模糊查找
*
* @param searchText 查找关键字
* @return 查询结果
*/
@Override
public R queryBySearchText(String searchText){
return R.ok().setData(this.productMapper.queryBySearchText(searchText));
}
/**
* 统计总行数
*
* @param product 筛选条件
* @return 查询结果
*/
@Override
public R getCount(Product product) {
return R.ok().setData(this.productMapper.count(product));
}
/**
* 新增数据
*
* @param product 实例对象
* @return 实例对象
*/
@Override
public R insert(Product product) {
if(product.getId()!=null && this.productMapper.queryById( product.getId() ) != null )
return R.error("已经存在");
this.productMapper.insert(product);
return R.ok().setData(product);
}
/**
* 修改数据
*
* @param product 实例对象
* @return 实例对象
*/
@Override
public R update(Product product) {
this.productMapper.update(product);
return R.ok().setData(this.queryById(product.getId()));
}
/**
* 通过主键删除数据
*
* @param id 主键
* @return 是否成功
*/
@Override
public R deleteById(Integer id) {
try{
this.productMapper.deleteById(id);
return R.ok();
} catch (Exception e)
{
e.printStackTrace();
return R.exp().setData(e.getMessage());
}
}
}
在服务消费者中对产品列表的提供者进行调用
public class ProductController
{
private final String URLPREFIX = "http://cloud-provider/product";
@Autowired
private RestTemplate restTemplate;
/**
* 全查询
*
* @param product 筛选条件
* @return 查询结果
*/
@GetMapping
public R queryAll(Product product)
{
return restTemplate.getForObject(getUrl(URLPREFIX,product), R.class);
}
/**
* 分页查询
*
* @param product 筛选条件
* @param pageSize 每页多少航
* @param page 第几页
* @return 查询结果
*/
@GetMapping("/page")
public R queryAllPage(Product product, Integer pageSize, Integer page)
{
String url = UriComponentsBuilder.fromHttpUrl(URLPREFIX+"/page")
.queryParam("pageSize", pageSize)
.queryParam("page", page)
.toUriString();
return restTemplate.getForObject(getPageUrl(url,product), R.class);
}
/**
* 模糊查找
*
* @param searchText 查找关键字
* @return 查询结果
*/
@GetMapping("/search/{searchText}")
public R queryBySearchText(@PathVariable("searchText") String searchText)
{
return restTemplate.getForObject(URLPREFIX+"/search/"+searchText, R.class);
}
/**
* 通过主键查询单条数据
*
* @param id 主键
* @return 单条数据
*/
@GetMapping("{id}")
public R queryById(@PathVariable("id") Integer id)
{
System.out.println("productId="+id);
return restTemplate.getForObject(URLPREFIX+"/"+id, R.class);
}
/**
* 统计总行数
*
* @param product 筛选条件
* @return 查询结果
*/
@GetMapping("/count")
public R getCount(Product product)
{
String url = UriComponentsBuilder.fromHttpUrl(URLPREFIX+"/count")
.toUriString();
return restTemplate.getForObject(getUrl(url,product), R.class);
}
/**
* 新增数据
*
* @param product 实体
* @return 新增结果
*/
@PostMapping
public R add(@RequestBody Product product)
{
return restTemplate.postForObject(URLPREFIX,product, R.class);
}
/**
* 更新数据
*
* @param product 实体
* @return 更新结果
*/
@PutMapping
public R update(@RequestBody Product product)
{
restTemplate.put(URLPREFIX,product);
return R.ok();
}
/**
* 删除数据
*
* @param id 主键
* @return 删除是否成功
*/
@DeleteMapping
public R deleteById(String id)
{
ResponseEntity
System.out.println(resultEntity.getBody());
return resultEntity.getBody();
}
}
4.2.3 测试
五、实验小结
实验中遇到的问题及解决过程 无实验中产生的错误及原因分析 无实验体会和收获。 无
相关文章
发表评论