【软构件】实验6 Spring Cloud组件

一、目的

实验目的:

掌握Spring Cloud组件模型及编程框架

实验要求:

独立完成实验操作,并撰写实验报告

实验内容:

1. 搭建Spring Cloud框架

使用原生Spring Cloud或Spring Cloud Alibaba搭建一个简单的Spring
Cloud框架,组件尽可能包含五个核心组件:注册中心、负载均衡、熔断降级、路由管关、配置中心。

2. 产品列表

在上述Spring
Cloud框架中,实现产品列表的功能,要求合理设计微服务、组件,综合考虑独立性、安全性、可扩展性等因素。

二、实验内容与设计思想

2.1 搭建Spring Cloud框架
  1. 搭建父项目

  2. 搭建eureka-server注册中心

  3. 搭建提供者服务

  4. 搭建消费者服务

  5. 实现服务之间的调用

2.2 产品列表
  1. 建立产品数据库和表

  2. 添加product表的controller,service,serviceImpl,domain,mapper到服务提供者中

  3. 在服务消费者中对产品列表的提供者进行调用

三、实验使用环境

平台:win10

软件:idea

四、实验步骤和调试过程

4.1 搭建Spring Cloud框架
4.1.1 实验步骤
  1. 搭建父项目

  2. 搭建eureka-server注册中心

添加依赖:

  • springweb
  • eureka 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
  1. 搭建提供者服务

添加依赖:

  • springweb
  • eureka discovery client
<!--Hystrix-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
    <version>2.2.10.RELEASE</version>
</dependency>

启动类加上注解:

@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()方法";
    }
}
  1. 搭建消费者服务
    添加依赖:
  • springweb
  • eureka discovery client
  • openfiegn
<!--Hystrix-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
    <version>2.2.10.RELEASE</version>
</dependency>

启动类加上注解:

@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<HelloService>
{
    @Override
    public HelloService create(Throwable cause)
    {
        return new HelloService()
        {
            @Override
            public String getProduct()
            {
                return "降级发生";
            }

            @Override
            public String hystrix()
            {
                return "降级发生";
            }
        };
    }
}
  1. 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<Product> queryAll(Product product);

        /**
         * 分页查询
         *
         * @param product 查询条件
         * @param pageSize 每页多少航
         * @param page 第几页
         * @return 对象列表
         */
        List<Product> queryAllPage(@Param("product")Product product,
        @Param("pageSize") Integer pageSize ,@Param("page") Integer page);

        /**
         * 模糊查找
         *
         * @param searchText 查找关键字
         * @return 查询结果
         */
         List<Product> queryBySearchText(String searchText);

        /**
         * 统计总行数
         *
         * @param product 查询条件
         * @return 总行数
         */
        long count(Product product);

        /**
         * 新增数据
         *
         * @param product 实例对象
         * @return 影响行数
         */
        int insert(Product product);

        /**
         * 批量新增数据(MyBatis原生foreach方法)
         *
         * @param entities List<Product> 实例对象列表
         * @return 影响行数
         */
        int insertBatch(@Param("entities") List<Product> entities);

        /**
         * 批量新增或按主键更新数据(MyBatis原生foreach方法)
         *
         * @param entities List<Product> 实例对象列表
         * @return 影响行数
         * @throws org.springframework.jdbc.BadSqlGrammarException 入参是空List的时候会抛SQL语句错误的异常,请自行校验入参
         */
        int insertOrUpdateBatch(@Param("entities") List<Product> 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<R> resultEntity = restTemplate.exchange(URLPREFIX+"?id="+id, HttpMethod.DELETE, null,R.class);
        System.out.println(resultEntity.getBody());
        return resultEntity.getBody();
    }
}
4.2.3 测试

五、实验小结

  1. 实验中遇到的问题及解决过程
  2. 实验中产生的错误及原因分析
  3. 实验体会和收获。
  • 19
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值