• 微服务第一轮


    课程文档

    目录

    一、业务流程

    1、登录

    Controller中的接口: 

     Service中的实现impl:

    Service中的实现impl所继承的接口IService(各种方法): 

    VO:

     DTO:

    2、搜索商品

    ​Controller中的接口:

    3、购物车

    ​Controller中的接口:

    二、拆分商品服务

    1. 创建新module - maven模块,并引入依赖

    2. 新建包com.hmall.xx(业务名),添加和修改启动类,新建mapper包、domain包 - service包 - controller包

    3. 拷贝并修改yaml配置文件到resources中,分别修改 端口号、服务名称、datasource(需创建sql datebase)、swagger接口文档说明与controller扫描包

    4.domain,mapper,service,controller包代码 

    5. 刷新maven,添加该业务模块启动项到Services中,并把Active profiles 修改为 local

    6. 运行,在访问地址后面添加doc.html访问swagger接口文档,进行调试

    ​三、拆分购物车服务

    四、服务调用(RPC)

    RestTemplate 

    远程直接调用

    五、Nacos注册中心

    服务注册

     1、添加依赖,配置Nacos

     2、启动服务,访问http://192.168.150.101:8848/nacos/控制台,可以发现服务注册成功:

    ​服务发现

    1、  添加依赖,配置Nacos

     2、根据负载均衡算法发现并调用方法


    一、业务流程

    1、登录

    Controller中的接口: 

    1. @Api(tags = "用户相关接口")
    2. @RestController
    3. @RequestMapping("/users")
    4. @RequiredArgsConstructor
    5. public class UserController {
    6. private final IUserService userService;
    7. @ApiOperation("用户登录接口")
    8. @PostMapping("login")
    9. public UserLoginVO login(@RequestBody @Validated LoginFormDTO loginFormDTO){
    10. return userService.login(loginFormDTO);
    11. }
    12. }

     Service中的实现impl:

    1. public interface IUserService extends IService {
    2. UserLoginVO login(LoginFormDTO loginFormDTO);
    3. void deductMoney(String pw, Integer totalFee);
    4. }

    Service中的实现impl所继承的接口IService(各种方法): 

    1. public interface IService {
    2. int DEFAULT_BATCH_SIZE = 1000;
    3. default boolean save(T entity) {
    4. return SqlHelper.retBool(this.getBaseMapper().insert(entity));
    5. }
    6. @Transactional(
    7. rollbackFor = {Exception.class}
    8. )
    9. default boolean saveBatch(Collection entityList) {
    10. return this.saveBatch(entityList, 1000);
    11. }
    12. boolean saveBatch(Collection entityList, int batchSize);
    13. @Transactional(
    14. rollbackFor = {Exception.class}
    15. )
    16. default boolean saveOrUpdateBatch(Collection entityList) {
    17. return this.saveOrUpdateBatch(entityList, 1000);
    18. }
    19. boolean saveOrUpdateBatch(Collection entityList, int batchSize);
    20. }

    VO:

    1. @Data
    2. public class UserLoginVO {
    3. private String token;
    4. private Long userId;
    5. private String username;
    6. private Integer balance;
    7. }

     DTO:

    1. @Data
    2. @ApiModel(description = "登录表单实体")
    3. public class LoginFormDTO {
    4. @ApiModelProperty(value = "用户名", required = true)
    5. @NotNull(message = "用户名不能为空")
    6. private String username;
    7. @NotNull(message = "密码不能为空")
    8. @ApiModelProperty(value = "用户名", required = true)
    9. private String password;
    10. @ApiModelProperty(value = "是否记住我", required = false)
    11. private Boolean rememberMe = false;
    12. }

    2、搜索商品

    在首页搜索框输入关键字手机,点击搜索即可进入搜索列表页面: 

     Controller中的接口:

    1. @Api(tags = "搜索相关接口")
    2. @RestController
    3. @RequestMapping("/search")
    4. @RequiredArgsConstructor
    5. public class SearchController {
    6. private final IItemService itemService;
    7. @ApiOperation("搜索商品")
    8. @GetMapping("/list")
    9. public PageDTO search(ItemPageQuery query) {
    10. // 分页查询
    11. Page result = itemService.lambdaQuery()
    12. .like(StrUtil.isNotBlank(query.getKey()), Item::getName, query.getKey())
    13. .eq(StrUtil.isNotBlank(query.getBrand()), Item::getBrand, query.getBrand())
    14. .eq(StrUtil.isNotBlank(query.getCategory()), Item::getCategory, query.getCategory())
    15. .eq(Item::getStatus, 1)
    16. .between(query.getMaxPrice() != null, Item::getPrice, query.getMinPrice(), query.getMaxPrice())
    17. .page(query.toMpPage("update_time", false));
    18. // 封装并返回
    19. return PageDTO.of(result, ItemDTO.class);
    20. }
    21. }

    3、购物车

    在搜索到的商品列表中,点击按钮加入购物车,即可将商品加入购物车:

     加入成功后即可进入购物车列表页,查看自己购物车商品列表:

     Controller中的接口:

    1. @Api(tags = "购物车相关接口")
    2. @RestController
    3. @RequestMapping("/carts")
    4. @RequiredArgsConstructor
    5. public class CartController {
    6. private final ICartService cartService;
    7. @ApiOperation("添加商品到购物车")
    8. @PostMapping
    9. public void addItem2Cart(@Valid @RequestBody CartFormDTO cartFormDTO){
    10. cartService.addItem2Cart(cartFormDTO);
    11. }
    12. @ApiOperation("更新购物车数据")
    13. @PutMapping
    14. public void updateCart(@RequestBody Cart cart){
    15. cartService.updateById(cart);
    16. }
    17. @ApiOperation("删除购物车中商品")
    18. @DeleteMapping("{id}")
    19. public void deleteCartItem(@Param ("购物车条目id")@PathVariable("id") Long id){
    20. cartService.removeById(id);
    21. }
    22. @ApiOperation("查询购物车列表")
    23. @GetMapping
    24. public List queryMyCarts(){
    25. return cartService.queryMyCarts();
    26. }
    27. @ApiOperation("批量删除购物车中商品")
    28. @ApiImplicitParam(name = "ids", value = "购物车条目id集合")
    29. @DeleteMapping
    30. public void deleteCartItemByIds(@RequestParam("ids") List ids){
    31. cartService.removeByItemIds(ids);
    32. }
    33. }

    其中,查询购物车列表时,由于要判断商品最新的价格和状态,所以还需要查询商品信息,业务流程如下:

    二、拆分商品服务

    1. 创建新module - maven模块,并引入依赖

    创建新模块 

    选择maven模块,并设定JDK版本为11: 

     添加依赖

    1. "1.0" encoding="UTF-8"?>
    2. <project xmlns="http://maven.apache.org/POM/4.0.0"
    3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    4. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    5. <modelVersion>4.0.0modelVersion>
    6. <parent>
    7. <groupId>com.heimagroupId>
    8. <artifactId>hmallartifactId>
    9. <version>1.0.0version>
    10. parent>
    11. <groupId>org.qingshuigroupId>
    12. <artifactId>item-serviceartifactId>
    13. <properties>
    14. <maven.compiler.source>11maven.compiler.source>
    15. <maven.compiler.target>11maven.compiler.target>
    16. <project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
    17. properties>
    18. <dependencies>
    19. <dependency>
    20. <groupId>com.heimagroupId>
    21. <artifactId>hm-commonartifactId>
    22. <version>1.0.0version>
    23. dependency>
    24. <dependency>
    25. <groupId>org.springframework.bootgroupId>
    26. <artifactId>spring-boot-starter-webartifactId>
    27. dependency>
    28. <dependency>
    29. <groupId>mysqlgroupId>
    30. <artifactId>mysql-connector-javaartifactId>
    31. dependency>
    32. <dependency>
    33. <groupId>com.baomidougroupId>
    34. <artifactId>mybatis-plus-boot-starterartifactId>
    35. dependency>
    36. <dependency>
    37. <groupId>org.springframework.bootgroupId>
    38. <artifactId>spring-boot-testartifactId>
    39. <version>3.3.0version>
    40. <scope>testscope>
    41. dependency>
    42. <dependency>
    43. <groupId>junitgroupId>
    44. <artifactId>junitartifactId>
    45. <scope>testscope>
    46. dependency>
    47. <dependency>
    48. <groupId>org.junit.jupitergroupId>
    49. <artifactId>junit-jupiterartifactId>
    50. <scope>testscope>
    51. dependency>
    52. dependencies>
    53. <build>
    54. <finalName>${project.artifactId}finalName>
    55. <plugins>
    56. <plugin>
    57. <groupId>org.springframework.bootgroupId>
    58. <artifactId>spring-boot-maven-pluginartifactId>
    59. plugin>
    60. plugins>
    61. build>
    62. project>

    2. 新建包com.hmall.xx(业务名),添加和修改启动类,新建mapper包、domain包 - service包 - controller包

     新建包com.hmall.item: 

     编写启动类ItemApplication:

    1. @MapperScan("com.hmall.item.mapper")
    2. @SpringBootApplication
    3. public class ItemApplication {
    4. public static void main(String[] args) {
    5. SpringApplication.run(ItemApplication.class, args);
    6. }
    7. }

     新建mapper包、domain包 - service包 - controller包:

    3. 拷贝并修改yaml配置文件到resources中,分别修改 端口号、服务名称、datasource(需创建sql datebase)、swagger接口文档说明与controller扫描包

    从hm-service中拷贝:

    修改application.yaml为:

    1. server:
    2. port: 8081
    3. spring:
    4. application:
    5. name: item-service
    6. profiles:
    7. active: dev
    8. datasource:
    9. url: jdbc:mysql://${hm.db.host}:3306/hm-item?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai
    10. driver-class-name: com.mysql.cj.jdbc.Driver
    11. username: root
    12. password: ${hm.db.pw}
    13. mybatis-plus:
    14. configuration:
    15. default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler
    16. global-config:
    17. db-config:
    18. update-strategy: not_null
    19. id-type: auto
    20. logging:
    21. level:
    22. com.hmall: debug
    23. pattern:
    24. dateformat: HH:mm:ss:SSS
    25. file:
    26. path: "logs/${spring.application.name}"
    27. knife4j:
    28. enable: true
    29. openapi:
    30. title: 商品服务接口文档
    31. description: "信息"
    32. email: zhanghuyi@itcast.cn
    33. concat: 虎哥
    34. url: https://www.itcast.cn
    35. version: v1.0.0
    36. group:
    37. default:
    38. group-name: default
    39. api-rule: package
    40. api-rule-resources:
    41. - com.hmall.item.controller

    创建该模块的数据库: 

    4.domain,mapper,service,controller包代码 

    【1】domain包代码:dto、po、vo、(query)

    dto:数据传输对象 

    OrderDetailDTO: 

    1. @ApiModel(description = "订单明细条目")
    2. @Data
    3. @Accessors(chain = true)
    4. public class OrderDetailDTO {
    5. @ApiModelProperty("商品id")
    6. private Long itemId;
    7. @ApiModelProperty("商品购买数量")
    8. private Integer num;
    9. }

     ItemDTO:

    1. @Data
    2. @ApiModel(description = "商品实体")
    3. public class ItemDTO {
    4. @ApiModelProperty("商品id")
    5. private Long id;
    6. @ApiModelProperty("SKU名称")
    7. private String name;
    8. @ApiModelProperty("价格(分)")
    9. private Integer price;
    10. @ApiModelProperty("库存数量")
    11. private Integer stock;
    12. @ApiModelProperty("商品图片")
    13. private String image;
    14. @ApiModelProperty("类目名称")
    15. private String category;
    16. @ApiModelProperty("品牌名称")
    17. private String brand;
    18. @ApiModelProperty("规格")
    19. private String spec;
    20. @ApiModelProperty("销量")
    21. private Integer sold;
    22. @ApiModelProperty("评论数")
    23. private Integer commentCount;
    24. @ApiModelProperty("是否是推广广告,true/false")
    25. private Boolean isAD;
    26. @ApiModelProperty("商品状态 1-正常,2-下架,3-删除")
    27. private Integer status;
    28. }

     po:实体

    1. @Data
    2. @EqualsAndHashCode(callSuper = false)
    3. @Accessors(chain = true)
    4. @TableName("item")
    5. public class Item implements Serializable {
    6. private static final long serialVersionUID = 1L;
    7. /**
    8. * 商品id
    9. */
    10. @TableId(value = "id", type = IdType.AUTO)
    11. private Long id;
    12. /**
    13. * SKU名称
    14. */
    15. private String name;
    16. /**
    17. * 价格(分)
    18. */
    19. private Integer price;
    20. /**
    21. * 库存数量
    22. */
    23. private Integer stock;
    24. /**
    25. * 商品图片
    26. */
    27. private String image;
    28. /**
    29. * 类目名称
    30. */
    31. private String category;
    32. /**
    33. * 品牌名称
    34. */
    35. private String brand;
    36. /**
    37. * 规格
    38. */
    39. private String spec;
    40. /**
    41. * 销量
    42. */
    43. private Integer sold;
    44. /**
    45. * 评论数
    46. */
    47. private Integer commentCount;
    48. /**
    49. * 是否是推广广告,true/false
    50. */
    51. @TableField("isAD")
    52. private Boolean isAD;
    53. /**
    54. * 商品状态 1-正常,2-下架,3-删除
    55. */
    56. private Integer status;
    57. /**
    58. * 创建时间
    59. */
    60. private LocalDateTime createTime;
    61. /**
    62. * 更新时间
    63. */
    64. private LocalDateTime updateTime;
    65. /**
    66. * 创建人
    67. */
    68. private Long creater;
    69. /**
    70. * 修改人
    71. */
    72. private Long updater;
    73. }

    vo:视图对象 

    在hm-service模块中没有与商品有关的代码。

    query:分页查询 

    1. @EqualsAndHashCode(callSuper = true)
    2. @Data
    3. @ApiModel(description = "商品分页查询条件")
    4. public class ItemPageQuery extends PageQuery {
    5. @ApiModelProperty("搜索关键字")
    6. private String key;
    7. @ApiModelProperty("商品分类")
    8. private String category;
    9. @ApiModelProperty("商品品牌")
    10. private String brand;
    11. @ApiModelProperty("价格最小值")
    12. private Integer minPrice;
    13. @ApiModelProperty("价格最大值")
    14. private Integer maxPrice;
    15. }

    【2】mapper包代码 :mapper接口 及mapper.xml文件

    1. public interface ItemMapper extends BaseMapper {
    2. @Update("UPDATE item SET stock = stock - #{num} WHERE id = #{itemId}")
    3. void updateStock(OrderDetailDTO orderDetail);
    4. }

    【3】 service包:service接口及实现类

    service接口: 

    1. public interface IItemService extends IService {
    2. void deductStock(List items);
    3. List queryItemByIds(Collection ids);
    4. }

     修改sqlStatement后的实现类:

    1. @Service
    2. public class ItemServiceImpl extends ServiceImpl implements IItemService {
    3. @Override
    4. public void deductStock(List items) {
    5. String sqlStatement = "com.hmall.mapper.item.ItemMapper.updateStock";
    6. boolean r = false;
    7. try {
    8. r = executeBatch(items, (sqlSession, entity) -> sqlSession.update(sqlStatement, entity));
    9. } catch (Exception e) {
    10. throw new BizIllegalException("更新库存异常,可能是库存不足!", e);
    11. }
    12. if (!r) {
    13. throw new BizIllegalException("库存不足!");
    14. }
    15. }
    16. @Override
    17. public List queryItemByIds(Collection ids) {
    18. return BeanUtils.copyList(listByIds(ids), ItemDTO.class);
    19. }
    20. }

    【4】controller包

    1. @Api(tags = "商品管理相关接口")
    2. @RestController
    3. @RequestMapping("/items")
    4. @RequiredArgsConstructor
    5. public class ItemController {
    6. private final IItemService itemService;
    7. @ApiOperation("分页查询商品")
    8. @GetMapping("/page")
    9. public PageDTO queryItemByPage(PageQuery query) {
    10. // 1.分页查询
    11. Page result = itemService.page(query.toMpPage("update_time", false));
    12. // 2.封装并返回
    13. return PageDTO.of(result, ItemDTO.class);
    14. }
    15. @ApiOperation("根据id批量查询商品")
    16. @GetMapping
    17. public List queryItemByIds(@RequestParam("ids") List ids){
    18. return itemService.queryItemByIds(ids);
    19. }
    20. @ApiOperation("根据id查询商品")
    21. @GetMapping("{id}")
    22. public ItemDTO queryItemById(@PathVariable("id") Long id) {
    23. return BeanUtils.copyBean(itemService.getById(id), ItemDTO.class);
    24. }
    25. @ApiOperation("新增商品")
    26. @PostMapping
    27. public void saveItem(@RequestBody ItemDTO item) {
    28. // 新增
    29. itemService.save(BeanUtils.copyBean(item, Item.class));
    30. }
    31. @ApiOperation("更新商品状态")
    32. @PutMapping("/status/{id}/{status}")
    33. public void updateItemStatus(@PathVariable("id") Long id, @PathVariable("status") Integer status){
    34. Item item = new Item();
    35. item.setId(id);
    36. item.setStatus(status);
    37. itemService.updateById(item);
    38. }
    39. @ApiOperation("更新商品")
    40. @PutMapping
    41. public void updateItem(@RequestBody ItemDTO item) {
    42. // 不允许修改商品状态,所以强制设置为null,更新时,就会忽略该字段
    43. item.setStatus(null);
    44. // 更新
    45. itemService.updateById(BeanUtils.copyBean(item, Item.class));
    46. }
    47. @ApiOperation("根据id删除商品")
    48. @DeleteMapping("{id}")
    49. public void deleteItemById(@PathVariable("id") Long id) {
    50. itemService.removeById(id);
    51. }
    52. @ApiOperation("批量扣减库存")
    53. @PutMapping("/stock/deduct")
    54. public void deductStock(@RequestBody List items){
    55. itemService.deductStock(items);
    56. }
    57. }

    5. 刷新maven,添加该业务模块启动项到Services中,并把Active profiles 修改为 local

    6. 运行,在访问地址后面添加doc.html访问swagger接口文档,进行调试

     三、拆分购物车服务

    同上 

    四、服务调用(RPC)

    背景: 

    在拆分的时候,我们发现一个问题:就是购物车业务中需要查询商品信息,但商品信息查询的逻辑全部迁移到了item-service服务,导致我们无法查询。(也就是一个模块要进行查询但是查询代码都在另外一个模块) 

    那么问题来了:我们该如何跨服务调用,准确的说,如何在cart-service中获取item-service服务中的提供的商品数据呢? 

     因此,现在查询购物车列表的流程变成了这样:

     假如我们在cart-service中能模拟浏览器,发送http请求到item-service,是不是就实现了跨微服务的远程调用了呢(也就是用Java代码发送Http请求)

    RestTemplate 

    Spring给我们提供了一个RestTemplate的API,可以方便的实现Http请求的发送。 

    其中提供了大量的方法,方便我们发送Http请求,例如: 

    我们在cart-service服务中定义一个配置类 

     先将RestTemplate注册为一个Bean:

    1. package com.hmall.cart.config;
    2. import org.springframework.context.annotation.Bean;
    3. import org.springframework.context.annotation.Configuration;
    4. import org.springframework.web.client.RestTemplate;
    5. @Configuration
    6. public class RemoteCallConfig {
    7. @Bean
    8. public RestTemplate restTemplate() {
    9. return new RestTemplate();
    10. }
    11. }

    远程直接调用

     在这个过程中,item-service提供了查询接口,cart-service利用Http请求调用该接口。因此item-service可以称为服务的提供者,而cart-service则称为服务的消费者或服务调用者。

    五、Nacos注册中心

    背景: 

    RPC通过Http请求实现了跨微服务的远程调用,这种手动发送Http请求的方式存在一些问题。 

    试想一下,假如商品微服务被调用较多,为了应对更高的并发,我们进行了多实例部署,如图: 

    此时,每个item-service的实例其IP或端口不同,问题来了:

    • item-service这么多实例,cart-service如何知道每一个实例的地址?

    • http请求要写url地址,cart-service服务到底该调用哪个实例呢?

    • 如果在运行过程中,某一个item-service实例宕机,cart-service依然在调用该怎么办?

    • 如果并发太高,item-service临时多部署了N台实例,cart-service如何知道新实例的地址?

    这时我们需要注册中心 (其实也是建立一个连接池)

    流程如下:

    • 服务启动时就会注册自己的服务信息(服务名、IP、端口)到注册中心

    • 调用者可以从注册中心订阅想要的服务,获取服务对应的实例列表(1个服务可能多实例部署)

    • 调用者自己对实例列表负载均衡,挑选一个实例

    • 调用者向该实例发起远程调用

    当服务提供者的实例宕机或者启动新实例时,调用者如何得知呢?

    • 服务提供者会定期向注册中心发送请求,报告自己的健康状态(心跳请求)

    • 当注册中心长时间收不到提供者的心跳时,会认为该实例宕机,将其从服务的实例列表中剔除

    • 当服务有新实例启动时,会发送注册服务请求,其信息会被记录在注册中心的服务实例列表

    • 当注册中心服务列表变更时,会主动通知微服务,更新本地服务列表

    服务注册

    将模块(微服务)注册到Nacos 

     1、添加依赖,配置Nacos

    item-servicepom.xml中添加依赖:

    1. <dependency>
    2. <groupId>com.alibaba.cloudgroupId>
    3. <artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
    4. dependency>

     在item-serviceapplication.yml中添加nacos地址配置:

    将nacos地址修改为自己的虚拟机地址 

    1. spring:
    2. application:
    3. name: item-service # 服务名称
    4. cloud:
    5. nacos:
    6. server-addr: 192.168.150.101:8848 # nacos地址

     2、启动服务,访问http://192.168.150.101:8848/nacos/控制台,可以发现服务注册成功:

     服务发现

    服务的消费者要去nacos订阅服务,这个过程就是服务发现 。

    1、  添加依赖,配置Nacos

    服务发现除了要引入nacos依赖以外,由于还需要负载均衡,因此要引入SpringCloud提供的LoadBalancer依赖。

    我们在cart-service中的pom.xml中添加下面的依赖:

    1. <dependency>
    2. <groupId>com.alibaba.cloudgroupId>
    3. <artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
    4. dependency>

     cart-serviceapplication.yml中添加nacos地址配置:

    1. spring:
    2. cloud:
    3. nacos:
    4. server-addr: 192.168.150.101:8848

     2、根据负载均衡算法发现并调用方法

    接下来,服务调用者cart-service就可以去订阅item-service服务了。不过item-service有多个实例,而真正发起调用时只需要知道一个实例的地址。

    因此,服务调用者必须利用负载均衡的算法,从多个实例中挑选一个去访问。常见的负载均衡算法有:

    • 随机

    • 轮询

    • IP的hash

    • 最近最少访问

    • ...

    这里我们可以选择最简单的随机负载均衡。

    另外,服务发现需要用到一个工具,DiscoveryClient,SpringCloud已经帮我们自动装配,我们可以直接注入使用:

     

     接下来,我们就可以对原来的远程调用做修改了,之前调用时我们需要写死服务提供者的IP和端口:

     但现在不需要了,我们通过DiscoveryClient发现服务实例列表,然后通过负载均衡算法,选择一个实例去调用:

    六、OpenFeign 

    快速入门

    OpenFeign其作用就是基于SpringMVC的常见注解,轻松的实现http请求的发送。 

    1、引入依赖,包括OpenFeign和负载均衡组件SpringCloudLoadBalancer 

    1. <dependency>
    2. <groupId>org.springframework.cloudgroupId>
    3. <artifactId>spring-cloud-starter-openfeignartifactId>
    4. dependency>
    5. <dependency>
    6. <groupId>org.springframework.cloudgroupId>
    7. <artifactId>spring-cloud-starter-loadbalancerartifactId>
    8. dependency>

    2、通过@EnableFeignClients注解,启用OpenFeign功能

    接下来,我们在cart-serviceCartApplication启动类上添加注解,启动OpenFeign功能: 

    3、编写OpenFeign客户端 

    cart-service中,定义一个新的接口,编写Feign客户端:  

    1. package com.hmall.cart.client;
    2. import com.hmall.cart.domain.dto.ItemDTO;
    3. import org.springframework.cloud.openfeign.FeignClient;
    4. import org.springframework.web.bind.annotation.GetMapping;
    5. import org.springframework.web.bind.annotation.RequestParam;
    6. import java.util.List;
    7. @FeignClient("item-service") //根据服务名称取注册中心
    8. public interface ItemClient {
    9. @GetMapping("/items")
    10. List queryItemByIds(@RequestParam("ids") Collection ids);
    11. }

    这里只需要声明接口,无需实现方法。接口中的几个关键信息:

    • @FeignClient("item-service") :声明服务名称

    • @GetMapping :声明请求方式

    • @GetMapping("/items") :声明请求路径

    • @RequestParam("ids") Collection ids :声明请求参数

    • List :返回值类型

    有了上述信息,OpenFeign就可以利用动态代理帮我们实现这个方法,并且向http://item-service/items发送一个GET请求,携带ids为请求参数,并自动将返回值处理为List

    我们只需要直接调用这个方法,即可实现远程调用了。

    连接池

    Feign底层发起http请求,依赖于其它的框架。其底层支持的http客户端实现包括:

    • HttpURLConnection:默认实现,不支持连接池

    • Apache HttpClient :支持连接池

    • OKHttp:支持连接池

    因此我们通常会使用带有连接池的客户端来代替默认的HttpURLConnection(效率不好)。比如,我们使用OK Http.

     1、引入依赖、开启连接池

    1. <dependency>
    2. <groupId>io.github.openfeigngroupId>
    3. <artifactId>feign-okhttpartifactId>
    4. dependency>

    cart-serviceapplication.yml配置文件中开启Feign的连接池功能: 

    1. feign:
    2. okhttp:
    3. enabled: true # 开启OKHttp功能

     2、实现

    Debug方式启动cart-service,请求一次查询我的购物车方法(发起请求),可以发现这里底层的实现已经改为OkHttpClient

     七、日志输出

    OpenFeign只会在FeignClient所在包的日志级别为DEBUG时,才会输出日志。而且其日志级别有4级:

    • NONE:不记录任何日志信息,这是默认值。

    • BASIC:仅记录请求的方法,URL以及响应状态码和执行时间

    • HEADERS:在BASIC的基础上,额外记录了请求和响应的头信息

    • FULL:记录所有请求和响应的明细,包括头信息、请求体、元数据。

    Feign默认的日志级别就是NONE,所以默认我们看不到请求日志。

     定义日志级别

    在hm-api模块下新建一个配置类,定义Feign的日志级别: 

     配置

    接下来,要让日志级别生效,还需要配置这个类。有两种方式:

    • 局部生效:在某个FeignClient中配置,只对当前FeignClient生效

    @FeignClient(value = "item-service", configuration = DefaultFeignConfig.class)
    • 全局生效:在@EnableFeignClients中配置,针对所有FeignClient生效。

    @EnableFeignClients(defaultConfiguration = DefaultFeignConfig.class)

     日志格式:

    1. 17:35:32:148 DEBUG 18620 --- [nio-8082-exec-1] com.hmall.api.client.ItemClient : [ItemClient#queryItemByIds] ---> GET http://item-service/items?ids=100000006163 HTTP/1.1
    2. 17:35:32:148 DEBUG 18620 --- [nio-8082-exec-1] com.hmall.api.client.ItemClient : [ItemClient#queryItemByIds] ---> END HTTP (0-byte body)
    3. 17:35:32:278 DEBUG 18620 --- [nio-8082-exec-1] com.hmall.api.client.ItemClient : [ItemClient#queryItemByIds] <--- HTTP/1.1 200 (127ms)
    4. 17:35:32:279 DEBUG 18620 --- [nio-8082-exec-1] com.hmall.api.client.ItemClient : [ItemClient#queryItemByIds] connection: keep-alive
    5. 17:35:32:279 DEBUG 18620 --- [nio-8082-exec-1] com.hmall.api.client.ItemClient : [ItemClient#queryItemByIds] content-type: application/json
    6. 17:35:32:279 DEBUG 18620 --- [nio-8082-exec-1] com.hmall.api.client.ItemClient : [ItemClient#queryItemByIds] date: Fri, 26 May 2023 09:35:32 GMT
    7. 17:35:32:279 DEBUG 18620 --- [nio-8082-exec-1] com.hmall.api.client.ItemClient : [ItemClient#queryItemByIds] keep-alive: timeout=60
    8. 17:35:32:279 DEBUG 18620 --- [nio-8082-exec-1] com.hmall.api.client.ItemClient : [ItemClient#queryItemByIds] transfer-encoding: chunked
    9. 17:35:32:279 DEBUG 18620 --- [nio-8082-exec-1] com.hmall.api.client.ItemClient : [ItemClient#queryItemByIds]
    10. 17:35:32:280 DEBUG 18620 --- [nio-8082-exec-1] com.hmall.api.client.ItemClient : [ItemClient#queryItemByIds] [{"id":100000006163,"name":"巴布豆(BOBDOG)柔薄悦动婴儿拉拉裤XXL码80片(15kg以上)","price":67100,"stock":10000,"image":"https://m.360buyimg.com/mobilecms/s720x720_jfs/t23998/350/2363990466/222391/a6e9581d/5b7cba5bN0c18fb4f.jpg!q70.jpg.webp","category":"拉拉裤","brand":"巴布豆","spec":"{}","sold":11,"commentCount":33343434,"isAD":false,"status":2}]
    11. 17:35:32:281 DEBUG 18620 --- [nio-8082-exec-1] com.hmall.api.client.ItemClient : [ItemClient#queryItemByIds] <--- END HTTP (369-byte body)

     

     

  • 相关阅读:
    SpringBoot整合WebSocket
    Linux jprobe的使用和原理
    【CV秋季划】生成对抗网络GAN有哪些研究和应用,如何循序渐进地学习好(2022年言有三一对一辅导)?...
    【数据库系统概论】第三章关系数据库标准语言SQL
    C语言指针速成下篇
    【华为上机真题 2022】字符串排序
    iOS——SDWebImage源码解析
    servlet数量太多,搞个轻量级springmvc
    开源机密计算平台:蓬莱-OpenHarmony
    rust的struct
  • 原文地址:https://blog.csdn.net/m0_74031434/article/details/139395945