网站做多久能盈利,公司域名是什么意思,许昌企业网站建设,企业网站管理源码随着项目的使用者越来越多#xff0c;项目承担的压力也会越来越大#xff0c;为了让我们的项目能服务更多的使用者#xff0c;我们不得不需要把我们的单体项目拆分成多个微服务#xff0c;就比如把一个商城系统拆分成用户系统#xff0c;商品系统#xff0c;订单系统项目承担的压力也会越来越大为了让我们的项目能服务更多的使用者我们不得不需要把我们的单体项目拆分成多个微服务就比如把一个商城系统拆分成用户系统商品系统订单系统购物车系统支付系统等等每个微服务都对应着自己的数据库别的服务不能访问别的微服务需要通过本微服务的service层方法才能获取。把这些微服务分配到多个Tomcat上使其能承担更多的压力。
但问题也来了在原来的单体架构中我们把所有系统的mapper写在同一个包中service写在同一个包中还有po、controller都各写在一个包中这样某个微服务想获取别的微服务的数据库中的数据直接引入对应的service层方法就可以了。但现在各个微服务分开了写成了多个module或project不能通过直接导入service层方法获取其他微服务的数据库数据了。
我就拿查询购物车列表时购物车微服务还需要调用商品微服务的service方法查询购物车中每个商品的详细信息。购物车微服务的数据库中只会存商品的基本信息
这是单体架构时的代码
Service
RequiredArgsConstructor //只对加上final的必须初始化的成员变量写入构造函数,不想写入构造函数的不写final就行
public class CartServiceImpl extends ServiceImplCartMapper, Cart implements ICartService {private final IItemService itemService;Overridepublic ListCartVO queryMyCarts() {// 1.查询我的购物车列表ListCart carts lambdaQuery().eq(Cart::getUserId, 1L/* UserContext.getUser()*/).list();if (CollUtils.isEmpty(carts)) {return CollUtils.emptyList();}// 2.转换VOListCartVO vos BeanUtils.copyList(carts, CartVO.class);// 3.处理VO中的商品信息handleCartItems(vos);// 4.返回return vos;}private void handleCartItems(ListCartVO vos) {
// 1.获取商品idSetLong itemIds vos.stream().map(CartVO::getItemId).collect(Collectors.toSet());// 2.查询商品ListItemDTO items itemService.queryItemByIds(itemIds);if (CollUtils.isEmpty(items)) {return;}// 3.转为 id 到 item的mapMapLong, ItemDTO itemMap items.stream().collect(Collectors.toMap(ItemDTO::getId, Function.identity()));// 4.写入vofor (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());} 原代码中只有handleCartItems方法中有对itemService的引用所以咱们只需要该handleCartItems方法中的itemService业务就可以了。
所以这时就需要微服务直接的远程调用远程调用有3种方法
方法一利用restTemplate不推荐
首先需要在启动类中声明resttemplate的bean
MapperScan(com.hmall.cart.mapper)
SpringBootApplication
public class CartServiceApplication {public static void main(String[] args) {SpringApplication.run(CartServiceApplication.class, args);}Beanpublic RestTemplate restTemplate(){return new RestTemplate();}
}
然后在需要改写的方法所在的类中也就是CartServiceImpl类中注入该bean,我用的构造器注入。
Service
RequiredArgsConstructor //只对加上final的必须初始化的成员变量写入构造函数,不想写入构造函数的不写final就行
public class CartServiceImpl extends ServiceImplCartMapper, Cart implements ICartService {//private final IItemService itemService;private final RestTemplate restTemplate;}
然后改写handleCartItems方法
private void handleCartItems(ListCartVO vos) {// 1.获取商品idSetLong itemIds vos.stream().map(CartVO::getItemId).collect(Collectors.toSet());// 2.查询商品//ListItemDTO items itemService.queryItemByIds(itemIds);//2.1 利用restTemplate发送http请求HashMapString,Object mapnew HashMap();map.put(ids, itemIds.stream().map(String::valueOf).collect(Collectors.joining(,)));ResponseEntityListItemDTO response restTemplate.exchange(http://localhost:8081/items?ids{ids}, //请求路径HttpMethod.GET, //请求方式null, //请求体可以为空//ItemDTO.class, //返回体的类型,应该返回ItemDTO的集合字节码不能使用泛型所以不能直接传list集合new ParameterizedTypeReferenceListItemDTO() {}, //这里传的是个对象可以用泛型//Map.of(ids,CollUtils.join(itemIds,,))map);//2.2 解析响应if (!response.getStatusCode().is2xxSuccessful()) {//查询失败return;}ListItemDTO items response.getBody();if (CollUtils.isEmpty(items)) {return;}// 3.转为 id 到 item的mapMapLong, ItemDTO itemMap items.stream().collect(Collectors.toMap(ItemDTO::getId, Function.identity()));// 4.写入vofor (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());}
注意restTemplate.exchange方法中的最后一个map参数如果你用的是jdk11及以上就可以直接写Map.of。我用的jdk8没有Map.of方法所以我提前声明一个hashMap并用stream流把itemIds转换成String形式并用逗号隔开。
由代码可知这中方式访问其他微服务的路径是写死的所以又有问题了我们在开发微服务时会有生产环境、测试环境等等而且微服务一般都是集群部署一个微服务会部署很多个端口后然后有nginx负载均衡转发给其中的一个 并且如果其中一个端口号对应的服务宕机了应该是不需要重启服务就自动有本集群中其他端口号的服务立刻顶上由这些原因可见restTemplate是很不方便的。
方法二nacos 购物车微服务的name为cart-service商品微服务的name为item-service nacos是个注册中心我简单说一下nacos在远程调用过程中起到的作用每个微服务在启动时都会向nacos注册本微服务的信息包括端口号本服务的名字等等然后当购物车微服务想查询商品详细信息时会根据微服务名字item-service从nacos中拉取商品微服务的相关信息包括端口号等等当商品微服务在nacos中注册了多个端口号的服务时nacos会根据负载均衡原则帮你选出一个来给你提高服务。
首先需要引入nacos的依赖
!-- nacos--dependencygroupIdcom.alibaba.cloud/groupIdartifactIdspring-cloud-starter-alibaba-nacos-discovery/artifactId/dependency 然后在application.yaml文件中写入nacos的地址地址看你启动nacos的IP 别忘了开启nacos
spring:cloud:nacos:server-addr: 127.0.0.1:8848 然后代码
Service
RequiredArgsConstructor //只对加上final的必须初始化的成员变量写入构造函数,不想写入构造函数的不写final就行
public class CartServiceImpl extends ServiceImplCartMapper, Cart implements ICartService {//private final IItemService itemService;private final RestTemplate restTemplate;private final DiscoveryClient discoveryClient;private void handleCartItems(ListCartVO vos) {//1.获取商品idSetLong itemIds vos.stream().map(CartVO::getItemId).collect(Collectors.toSet());// 2.查询商品//2.1根据服务名称获取服务的实例列表ListServiceInstance instances discoveryClient.getInstances(item-service);if (CollUtil.isEmpty(instances)){return;}//2.2手写负载均衡从实例列表中挑一个ServiceInstance serviceInstance instances.get(RandomUtil.randomInt(instances.size()));//2.3 利用restTemplate发送http请求HashMapString,Object mapnew HashMap();map.put(ids, itemIds.stream().map(String::valueOf).collect(Collectors.joining(,)));ResponseEntityListItemDTO response restTemplate.exchange(serviceInstance.getUri()/items?ids{ids}, //请求路径HttpMethod.GET, //请求方式null, //请求体可以为空//ItemDTO.class, //返回体的类型,应该返回ItemDTO的集合字节码不能使用泛型所以不能直接传list集合new ParameterizedTypeReferenceListItemDTO() {}, //这里传的是个对象可以用泛型//Map.of(ids,CollUtils.join(itemIds,,))map);//2.4 解析响应if (!response.getStatusCode().is2xxSuccessful()) {//查询失败return;}ListItemDTO items response.getBody();if (CollUtils.isEmpty(items)) {return;}// 3.转为 id 到 item的mapMapLong, ItemDTO itemMap items.stream().collect(Collectors.toMap(ItemDTO::getId, Function.identity()));// 4.写入vofor (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());}
}
这样我们的路径就不是写死的了但是原来本是
ListItemDTO items itemService.queryItemByIds(itemIds); 这一行代码的事现在呢写了10多行代码才解决。如果不同微服务之间都需要互相调用那岂不是每个方法都需要这么写繁琐死所以我们有了简明一点的第三种方法
第三种方法openFeign
feign是一个声明式的http客户端其作用是帮助我们优雅的实现http请求的发送。
声明式意思是我们只需要把发请求所需要的信息声明好剩下的我们不需要去管全交给Feign去做。
步骤
首先我们需要创建一个与购物车系统和商品系统同层次的系统名字随便命名我用的是hm-api。
然后在pom文件中引入相关依赖
!-- openfeign--dependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-starter-openfeign/artifactId/dependencydependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-starter-loadbalancer/artifactId/dependency!-- 引入okhttp连接池提高feign的性能--dependencygroupIdio.github.openfeign/groupIdartifactIdfeign-okhttp/artifactId/dependencydependencygroupIdio.swagger/groupIdartifactIdswagger-annotations/artifactIdversion1.6.6/version/dependency
然后在hm-api项目下创建clientdtoconfig包。client包中写的是远程调用时调用的方法dto是远程调用方法需要的实体类config包是对Feign的配置。 我们查看需要远程调用的代码部分
ListItemDTO items itemService.queryItemByIds(itemIds);
需要用到itemService.queryItemByIds方法我们先去找它的controller中对应的方法
RestController
RequestMapping(/items)
RequiredArgsConstructor
public class ItemController {private final IItemService itemService;GetMappingpublic CollectionItemDTO queryItemByIds(RequestParam(ids) CollectionLong ids){return itemService.queryItemByIds(ids);}
}
然后把该controller中对应的方法抽取出来写入hm-api中的client包中
package com.hmall.hmapi.client;import com.hmall.hmapi.dto.ItemDTO;
import com.hmall.hmapi.dto.OrderDetailDTO;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;import java.util.Collection;
import java.util.List;FeignClient(item-service)
public interface ItemClient {GetMapping(/items)ListItemDTO queryItemByIds(RequestParam(ids) CollectionLong ids);}FeignClient注解中写要调用的微服务的name。
然后把代码中用到的ItemDto实体类拷贝到hm-api中的dto包中。
然后代码
Service
RequiredArgsConstructor //只对加上final的必须初始化的成员变量写入构造函数,不想写入构造函数的不写final就行
public class CartServiceImpl extends ServiceImplCartMapper, Cart implements ICartService {//private final IItemService itemService;//private final RestTemplate restTemplate;//private final DiscoveryClient discoveryClient;private final ItemClient itemClient;private void handleCartItems(ListCartVO vos) {//openFeignSetLong itemIds vos.stream().map(CartVO::getItemId).collect(Collectors.toSet());ListItemDTO items itemClient.queryItemByIds(itemIds);if (CollUtils.isEmpty(items)) {return;}// 3.转为 id 到 item的mapMapLong, ItemDTO itemMap items.stream().collect(Collectors.toMap(ItemDTO::getId, Function.identity()));// 4.写入vofor (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());}}
} 现在代码就简短很多了。
因为每次远程调用访问数据库都需要建立一次连接损耗比较大所以我们建议引入一个连接池。连接池的依赖我们前面已经引入了现在需要做的只有开启连接池了现在让我们去调用远程调用的application.yaml文件中也就是cart-service服务中的application.yaml中写上以下几行配置就可以了。
feign:okhttp:enabled: true # 打开feign连接池配置