一、目的

实验目的:

掌握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

org.springframework.cloud

spring-cloud-starter-netflix-hystrix

2.2.10.RELEASE

启动类加上注解:

@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

org.springframework.cloud

spring-cloud-starter-netflix-hystrix

2.2.10.RELEASE

启动类加上注解:

@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 queryAll(Product product);

/**

* 分页查询

*

* @param product 查询条件

* @param pageSize 每页多少航

* @param page 第几页

* @return 对象列表

*/

List queryAllPage(@Param("product")Product product,

@Param("pageSize") Integer pageSize ,@Param("page") Integer page);

/**

* 模糊查找

*

* @param searchText 查找关键字

* @return 查询结果

*/

List queryBySearchText(String searchText);

/**

* 统计总行数

*

* @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 entities);

/**

* 批量新增或按主键更新数据(MyBatis原生foreach方法)

*

* @param entities List 实例对象列表

* @return 影响行数

* @throws org.springframework.jdbc.BadSqlGrammarException 入参是空List的时候会抛SQL语句错误的异常,请自行校验入参

*/

int insertOrUpdateBatch(@Param("entities") List entities);

/**

* 修改数据

*

* @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 resultEntity = restTemplate.exchange(URLPREFIX+"?id="+id, HttpMethod.DELETE, null,R.class);

System.out.println(resultEntity.getBody());

return resultEntity.getBody();

}

}

4.2.3 测试

五、实验小结

实验中遇到的问题及解决过程 无实验中产生的错误及原因分析 无实验体会和收获。 无

相关文章

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: