服务治理的问题

单体应用

这里以一个电商系统为例,编写简单的代码进行演示,创建一个Controller和三个Service。在Controller中调用这三个Service,模拟一个简单的购物流程。

创建OrderService(订单服务层,接口略,下面是实现类)

@Service
public class OrderServiceImpl implements OrderService {
    @Override
    public void createOrder() {
        System.out.println("创建订单数据");
    }
}

创建PayService(支付服务层)

@Service
public class PayServiceImpl implements PayService {
    @Override
    public void pay() {
        System.out.println("支付");
    }
}

创建StockService(库存服务层)

@Service
public class StockServiceImpl implements StockService {
    @Override
    public void updateStock() {
        System.out.println("修改库存");
    }
}

创建BuyController(购买控制器),调用上面三个服务层的方法

/*
    购买
 */
@RestController
public class BuyController {

    @Autowired
    private OrderService orderService;

    @Autowired
    private PayService payService;

    @Autowired
    private StockService stockService;

    /*
        这里应该用PostMapping,为了便于演示,使用GetMapping
     */
    @GetMapping("buy")
    public String buy() {
        //支付
        payService.pay();
        //操作库存
        stockService.updateStock();
        //生成订单数据
        orderService.createOrder();

        return "购买成功";
    }
}

重构为微服务架构

上面的示例是在一个购物业务流程中需要进行的操作,此时可以根据业务流程重构为微服务,将购买,支付,库存,订单拆分成4个模块,每个模块可视为是一个微服务。

拆分后微服务之间需要进行调用,这个是微服务架构比较常见的场景。此时在购买微服务中仍然需要调用其他三个微服务,我们把主动调用方叫做服务消费者(例如上面的Buy),把被调用方叫做服务的提供者(例如上面的pay,stock,order)。在不同的业务场景下,服务的消费者可能也是提供者,服务提供者可能也是消费者。为了方便演示,这里通过代码编写购买和支付之间的调用。下面两个都使用spring boot创建web模块。

1.新建支付模块

创建一个PayServiceImpl(接口略)

@Service
public class PayServiceImpl implements PayService {
    @Override
    public void pay() {
        System.out.println("支付操作");
    }
}

创建PayController,这里创建Controller的目的是为了可以被其他微服务调用。

/*
    支付服务
 */
@RestController
public class PayController {

    @Autowired
    private PayService payService;

    @GetMapping("pay")
    public String pay() {
        //支付操作
        payService.pay();

        return "支付成功";
    }
}

为了避免tomcat端口号冲突,将server.port端口号修改为8081

2.创建购买模块

要在购买模块中调用支付模块的PayController的pay()方法,此时是不能直接调用的,我们可以使用spring提供的RestTemplate类进行远程调用,RestTemplate简化了发起HTTP请求以及处理响应的过程,并且支持REST,下面来使用一下RestTemplate。这里创建一个配置类,将创建出来的RestTemplate对象放到spring容器中。

/*
    配置类
 */
@Configuration
public class BeanConfig {

    /*
        将RestTemple对象放入到spring容器中
     */
    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

创建BuyController,里面使用restTemplate远程调用支付模块的PayController的pay()方法

/*
    购买
 */
@RestController
public class BuyController {

    @Autowired
    private RestTemplate restTemplate;

    @GetMapping("buy")
    public String buy() {

        /*
            通过restTemplate对象远程调用支付模块的PayController的pay()方法
            参数1:要调用的远程方法的url地址
            参数2:方法的返回类型,这里因为传入了String.class,所以返回值是String类型
         */
        String msg = restTemplate.getForObject("http://localhost:8081/pay", String.class);
        
        return "购买成功:" + msg;
    }
}

将server.port端口号修改为8082,分别启动支付模块和购买模块,浏览器访问购买模块的buy,可以看到相应的提示信息。这样就完成了一个简单的微服务之间的调用功能。

目前的问题

将要调用的微服务的地址(localhost:8081/pay)写死到了代码中,倘若ip或者端口发生了变化,还需要修改代码,这样就非常不方便了。要想解决这个问题,需要使用对服务进行治理,即将这些地址记录起来,当微服务进行调用的时候,从记录的地方进行读取。下一节里面来介绍微服务的治理。