SpringCloud系列(1)---初试微服务

13 篇文章 0 订阅
5 篇文章 0 订阅

微服务


之前在写SpringBoot的笔记时,就有提及到SpringCloud。SpringCloud提供了微服务的开箱即用。微服务近年来非常火,到处都在说微服务。笔者也对微服务相当感兴趣,因为笔者在校期间(N年前)曾经和很多同学聊过,如果所有的应用并不是单体的,而是通过很多系统提供API这会变成怎么样,当时我就觉得这样能够做到分布式服务。因为服务是分离的,我们可以针对每一个不同的服务,调整负载均衡的设备数量,提供更多的资源给到负载较重的服务。但是在考虑的时候也发现了很多技术的问题,例如如何在多个服务器提供强一致性的事务管理,服务服务之前应该通过什么方式去调用(HTTP?NIO?AMQP?),太多太多的问题导致我认为当初这个想法是不切实际的,可能是以前技术水平并不高的原因吧··· 虽然现在也不高。庆幸的是,N年之后的今天高流量负载的互联网推动了微服务。当年我想的问题,至今已经有了非常多相应的解决方案,让我对微服务充满了未知的兴趣。

为什么我会选择写SpringCloud的笔记?目前有很多很优秀的微服务框架,例如阿里的dubbo。很简单因为dubbo有很详细的中文文档,想学或者忘记了可以查dubbo的文档,但是对比与SpringCloud,笔者认为dubbo性能会更佳,因为SpringCloud 主要的通信协议使用HTTP(REST),AMQP。但是笔者因为HTTP的短板是每次通信都是需要建立连接效率上还是有所欠缺,而dubbo是提倡使用基于NIO的通信,对于大吞吐量有非常大的优势。但是不管怎么说,SpringCloud也是相当优秀的框架,在近些年来SpringCloud的版本发布速度也是快得离谱,详细SpringCloud会越来越完善。而且SpringCloud基于SpringBoot提供给我们非常简单的配置和操作,学习一门分布式框架有助于你理解分布式的其他架构理论,毕竟打过仗再去看兵法,比纸上谈兵效果很好嘛····

其实在分布式最为困难的是,如何去换分服务。这是非常困难的,往往需要非常多经验的沉淀。笔者不才~ 但是个人觉得,按照服务器是否存在强一致性去划分比较好。因为网络是不稳定的、容易出错的,所以对于一些强一致性的业务最好不要分。不过,如果出现边界模糊、服务过于复杂你也得分,但是这样你就要在容错技术上做好充分的准备,如何做事务补偿机制、如何保证一致性都是等着你的问题。但是笔者对这部分还是有相当多的空白,所以笔者在外来一段时间会看一些关于领域驱动设计的书籍,如果觉得不错,也会CSDN写相关的博客,也会推荐相关的书籍。

写了那么多,第一次写这么多理论的东西。不管怎么样,我会在我的笔记尝试解答着一切,以后无论是其他开发者看到我的笔记也好,或者是我自己工作遇到这些技术需求也好,希望这些笔记可以帮助到我或者你们。

我们开始之前如果你对SpringBoot不熟悉,可以移步先看看SpringBoot【SpringBoot系列(1)---无配置文件配置基础1】,如果你连Spring都不熟悉,也可以先看看Spring。


一、初试微服务

先说说业务,目前笔者所在的团队主要是开发电商系统的,所以我将会以电商的业务作为业务模型,相信都网购过也不用多说业务上的东西了。

我们会创建三套服务,商品服务、用户服务、订单服务 其他什么仓储物流、促销系统、评价服务、团购等等就不搞了。简单明了去认识服务。

简单点,商品服务提供商品查询,有两个参数用户ID和商品ID,根据用户ID从用户服务中获得用户信息(通常都会添加商品访问日志用于数据分析、通过促销系统、验证优惠信息操作都需要用到当前登录的用户信息)由于方便,我们就不用Redis做用户登录的token缓存了,实际环境自己发挥·····

先创建两个项目,一个是商品服务系统、第二个是用户服务系统。


在用户服务系统我定义了两个接口,以下是接口的Controller:

@RestController
public class UserController {

    @Autowired
    private UserRepository userRepository;

    @GetMapping("/getUserById/{id}")
    public User getUserById(@PathVariable int id){
        return this.userRepository.getByUserId(id);
    }

    @PutMapping("/save")
    public User addUser(String username,String password,long balance){
        User user = new User(username,password,balance);
        this.userRepository.save(user);
        return user;
    }
}

在商品服务系统,我定义也定义了两个接口,以下是商品服务系统的Controller:

@RestController
public class ProductController {

    @Autowired
    private ProductRepository productRepository;

    @Autowired
    private RestTemplate restTemplate;

    @PutMapping("/add")
    public Product addProduct(String productName, String productDesc, long productPrice) {
        Product product = new Product(productName, productDesc, productPrice);
        return this.productRepository.save(product);
    }

    @GetMapping("/getProduct/{productId}/userId/{userId}")
    public Map<String, Object> getProduct(@PathVariable int productId, @PathVariable int userId) {
        Map<String, Object> map = new HashMap<>();
        String url = "http://localhost:8801/user/getUserById/" + userId;
        User user = this.restTemplate.getForEntity(url, User.class).getBody();
        Product product = this.productRepository.getByProductId(productId);
        map.put("user",user);
        map.put("product",product);
        return map;
    }

}

使用RestTemplate之前,必须定义相关的bean,以下是商品服务系统的配置类:

@SpringBootApplication
public class ProductsystemApplication {

   public static void main(String[] args) {
      SpringApplication.run(ProductsystemApplication.class, args);
   }

   @Bean
   public RestTemplate restTemplate(){
      return new RestTemplate();
   }

}

虽然我们上面的getProduct接口没有什么实际意义,但是已经体现了微服务的技术点。由于笔者没有分开两台计算机去开发两个项目,而是在同一台计算机使用不同的端口号运行两套系统,以下笔者当前两个项目的application.properties配置文件:

用户服务系统配置文件如下:

server.port=8801
server.context-path=/user

spring.datasource.url=jdbc:mysql://localhost:3306/shop_mall?charset=utf8mb4
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=tonyyan

商品服务系统配置文件如下:

server.port=8802
server.context-path=/product

spring.datasource.url=jdbc:mysql://localhost:3306/shop_mall?charset=utf8mb4
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=tonyyan

可以看到笔者两个项目都是连接相同的数据库,但是在实际的微服务上大可不必。由于数据库的连接池非常容易在极端场景打满,所以不同的服务可以使用不同的数据库作为持久化层,而且可以针对每个服务的特性去选择数据库也是非常常见的做法,如果数据量大,查询速度上有要求,也可以在这些服务系统上使用mongodb或者hbase等NOSQL数据作为持久化层。

测试接口:http://localhost:8802/product/getProduct/1/userId/1

JSON返回如下:

{
  "product": {
    "productId": 1,
    "productName": "Iphone",
    "productDesc": "new Iphone",
    "productPrice": 700000
  },
  "user": {
    "userId": 1,
    "username": "TONY",
    "userpwd": "TONYPWD",
    "balance": 1000000
  }
}


简单的我们已经可以发现几项问题:

1、我们的访问URL是写死的,如果目标服务有变,访问接口也需要变。

2、访问的服务器不是高可用的,更加不用说负载均衡了。


下一遍笔记将会解决上述两大问题。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值