黑马商城项目(三)微服务

一、单体架构

测试高并发软件

二、微服务

三、SpringCloud

四、微服务拆分

黑马商城模块:

服务拆分原则:

拆分服务:

独立project:

maven聚合:

拆分案例:

远程调用:

java 复制代码
package com.hmall.cart.config;

import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

@org.springframework.context.annotation.Configuration
public class Configuration {

    @Bean
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
}
java 复制代码
@Service
@RequiredArgsConstructor  //自动生成必备参数的构造函数
public class CartServiceImpl extends ServiceImpl<CartMapper, Cart> implements ICartService {

    /*private final IItemService itemService;*/

    // @Autowired  Spring不建议使用@Autowired
    private final RestTemplate restTemplate;

    /*Spring建议使用构造函数注入*/
/*    public CartServiceImpl(RestTemplate restTemplate){
        this.restTemplate=restTemplate;
    }*/
}
java 复制代码
 private void handleCartItems(List<CartVO> vos) {
        // 1.获取商品id
        Set<Long> itemIds = vos.stream().map(CartVO::getItemId).collect(Collectors.toSet());
        // 2.查询商品
        //List<ItemDTO> items = itemService.queryItemByIds(itemIds);
        //2.1 利用RestTemplate发送http请求,得到http响应
        ResponseEntity<List<ItemDTO>> response = restTemplate.exchange(
                "http://localhost:8081/items?ids={ids}",
                HttpMethod.GET,
                null,
                new ParameterizedTypeReference<List<ItemDTO>>() {
                }, //利用反射拿到这个对象上的泛型
                Map.of("ids", CollUtil.join(itemIds, ","))
        );
        //2.2解析响应
        if(!response.getStatusCode().is2xxSuccessful()){
            //查询失败 直接结束
            return;
        }
        List<ItemDTO> items = response.getBody();

        if (CollUtils.isEmpty(items)) {
            return;
        }
        // 3.转为 id 到 item的map
        Map<Long, ItemDTO> itemMap = items.stream().collect(Collectors.toMap(ItemDTO::getId, Function.identity()));
        // 4.写入vo
        for (CartVO v : vos) {
            ItemDTO item = itemMap.get(v.getItemId());
            if (item == null) {
                continue;
            }
            v.setNewPrice(item.getPrice());
            v.setStatus(item.getStatus());
            v.setStock(item.getStock());
        }
    }

逻辑:

五、服务治理

上文提到的远程调用带来的问题:

注册中心:

Nacos注册中心:

bash 复制代码
docker run -d \
--name nacos \
--env-file ./nacos/custom.env \
-p 8848:8848 \
-p 9848:9848 \
-p 9849:9849 \
--restart=always \
nacos/nacos-server:v2.1.0-slim

服务注册:

多实例部署:

服务发现:

六、OpenFeign

快速入门:



请求路径参数通过SpringMVC注解@GetMapping以及@RequestParam完成替代

请求参数通过调用该接口方法时传递的参数完成替代

返回值类型通过该接口定义的抽象方法的返回值类型完成替代

从而实现整体替代,简化代码

java 复制代码
package com.hmall.cart.client;

import com.hmall.cart.domain.dto.ItemDTO;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import java.util.Collection;
import java.util.List;

@FeignClient("item-service")
public interface ItemClient {
    @GetMapping("/items")
    List<ItemDTO> queryItemByIds(@RequestParam("ids") Collection<Long> ids);
}
java 复制代码
    private final ItemClient itemClient;    //自动注入

private void handleCartItems(List<CartVO> vos) {
        // 1.获取商品id
        Set<Long> itemIds = vos.stream().map(CartVO::getItemId).collect(Collectors.toSet());
        // 2.查询商品
        //2.1 根据服务名称获取服务的实例列表
     /*   List<ServiceInstance> instances = discoveryClient.getInstances("item-service");
        if(CollUtil.isEmpty(instances)){
            //判断为空 直接结束
            return;
        }
        //2.2 手写负载均衡,从实例列表中挑选一个实例
        ServiceInstance instance = instances.get(RandomUtil.randomInt(0, instances.size()));

        URI uri = instance.getUri(); //主机地址 http://localhost:8081


        //List<ItemDTO> items = itemService.queryItemByIds(itemIds);
        //2.3 利用RestTemplate发送http请求,得到http响应
        ResponseEntity<List<ItemDTO>> response = restTemplate.exchange(
                uri+"/items?ids={ids}",
                HttpMethod.GET,
                null,
                new ParameterizedTypeReference<List<ItemDTO>>() {
                }, //利用反射拿到这个对象上的泛型
                Map.of("ids", CollUtil.join(itemIds, ","))
        );
        //2.4解析响应
        if(!response.getStatusCode().is2xxSuccessful()){
            //查询失败 直接结束
            return;
        }
        List<ItemDTO> items = response.getBody();*/

        List<ItemDTO> items = itemClient.queryItemByIds(itemIds);

        if (CollUtils.isEmpty(items)) {
            return;
        }
        // 3.转为 id 到 item的map
        Map<Long, ItemDTO> itemMap = items.stream().collect(Collectors.toMap(ItemDTO::getId, Function.identity()));
        // 4.写入vo
        for (CartVO v : vos) {
            ItemDTO item = itemMap.get(v.getItemId());
            if (item == null) {
                continue;
            }
            v.setNewPrice(item.getPrice());
            v.setStatus(item.getStatus());
            v.setStock(item.getStock());
        }
    }

底层原理:

代码中通过 itemClient对象来调用接口方法时,实际上它是一个动态代理对象。

动态代理对象底层逻辑都是由 InvocationHandler 实现

这里的 FeignInvocationHandler 是ReflectiveFeign 的一个内部类,实现了 InvocationHandler 接口, InvocationHandler 接口中的invoke 方法就是起到了代理的作用 : 所有的被代理对象中的所有业务(方法) 都会通过 invoke 方法实现代理,除了一些基本方法(比如 equals, hashCode ,toString)

InvocationHandler 接口的实现类 FeignInvocationHandler 中重写对应invoke 从而实现代理的方法如下:

最后调用对应的方法发送请求(Client)

连接池:

OpenFeign底层发送请求使用的是 Client

OpenFeign整合并使用连接池步骤:

最佳实践:

拆分的最佳方式:

方法一:

方法二:

按照第二种方法实践:

日志输出:

七、服务拆分作业

相关推荐
loulanyue_2 小时前
从个人提效到团队可验证交付:中宏保险AI Coding流水线重塑与Qoder实战——读中宏保险AI架构总监王志宇2026云栖专场演讲
人工智能·架构
wewowilling3 小时前
认识微服务
java·运维·微服务·springcloud
Erishen3 小时前
Lume C11编译器内部架构:从树形解释器到双LLVM后端的设计之旅
架构·开源·编译器
沪上企服通4 小时前
历史账套科目重映射与规则版本重算:旧账梳理的“口径复位”工程
笔记·架构
亥时无人机管理系统5 小时前
无人机管理系统源码 | 私有化部署平台 航线规划飞行审批实时监控功能介绍
架构·私有化部署·无人机·低空经济·无人机巡检·无人机管理系统·一网统飞
这个DBA有点耶5 小时前
时序数据库InfluxDB高基数问题深度拆解:内存索引膨胀机制与OOM完整链路
数据库·架构·dba
阿里云云原生6 小时前
从页面渲染到智能体:ARMS Node.js 探针解锁 SSR + AI 全链路可观测
云原生
众链网络6 小时前
Agent 的成本大头不在“写“,在“判断“:拆解专用决策模型的三层架构
java·开发语言·人工智能·微服务
木子n16 小时前
软件架构设计实战-架构15.ISO 26262 架构设计要求:安全机制在架构里的落点
架构·autosar·功能安全·iso 26262