
5.8 改造coupon-user-service服务
5.8.1 添加依赖项和配置项
仿照coupone-template-service和coupon-calculation-service的改造步骤,添加eureka-client的依赖项到coupon-user-service项目下的pom.xml文件中,在com.broadview.coupon.user包路径下创建启动类CouponUserApplication。将coupon-template-service项目的配置文件application.yml复制到coupon-user-service项目中,在复制后的application.xml文件中我们只需更改应用名称和端口号,其他内容不变,相关代码如下:

5.8.2 声明RestTemplate
Spring提供了一个轻量级的HTTP调用工具类org.springframework.web.client. RestTemplate,我们可以在coupon-user-service项目中通过RestTemplate向其他应用发起HTTP调用。打开CouponUserApplication类,使用@Bean注解声明一个RestTemplate类的对象,具体实现代码如下:

5.8.3 改造findCoupon()方法——RestTemplate.exchange函数的用法
由于service层已经被拆分到各个微服务模块中,无法通过注入service对象的方式直接发起本地方法调用,所以我们需要通过RestTemplate发起远程调用。在本节中我们将分别用多种不同的集成方式来演示RestTemplate的用法。
接下来,我们通过三个步骤来改造UserServiceImpl类的findCoupon()方法,具体步骤如下。
1. 依赖注入
找到UserServiceImpl类,将无效引用删除以后,通过@Autowired注解将RestTemplate和LoadbalancerClient对象注入UserServiceImpl类中,具体代码如下:

RestTemplate类用来发起远程HTTP调用,LoadBalancerClient类可以通过负载均衡策略获取可用的服务节点的地址信息。
2. 添加寻址方法
在UserServiceImpl类中加入两个新方法,这两个方法分别用来拼接访问地址和组装HttpHeader信息,具体代码如下:


在上面的代码中,getUrl()方法的serviceId必须与目标服务的注册名称(即application.yml文件中定义的application.name属性)相同,LoadBalancerClient对象会基于服务发现获取的机器列表,通过负载均衡选定一个可用地址并返回。
3. 使用RestTemplate替换本地方法调用
最后一步是替换本地方法调用,通过resetTemplate的exchange()方法来发起一个HTTP调用,具体代码如下:


RestTemplate中的exchange()方法可以支持任意类型(GET、POST、DELETE和PUT)的HTTP请求,上面代码中传入exchange()方法的参数从左到右依次为:目标访问地址、HTTP请求类型、请求内容(header+body)和返回值类型。Exchange()方法不会直接返回业务对象,而是通过一个ResponseEntity对象将业务对象封装起来,我们可以通过getBody()方法从ResponseEntity对象中获取最终的返回结果。
5.8.4 改造requestCoupon()方法——getForObject函数的用法
打开UserServiceImpl类的requestCoupon()方法,使用RestTemplate.getForObject()方法替换当前方法内的本地调用,具体代码如下:

5.8.5 改造placeOrder()方法
UserServiceImpl类的placeOrder()方法同时对coupon-template-service和coupon-calculation-service发起了调用,下面分别使用getForObject()和postForObject()方法对两处方法调用进行替换,具体代码如下:

在调用coupon-tempalte-service的时候,我们没有将查询参数直接写入URL中,而是通过一个外部Map对象传递Request参数,RestTemplate可以自动把URL中定义的占位符(如代码中定义的{id})替换为Map中的参数,Map对象中的key就对应了URL中占位符的属性名。
5.8.6 启动项目并验证服务注册
我们分别启动eureka-server和eureka-server2两个注册中心,待注册中心启动完成后再依次启动coupon-template-service、coupon-calculator-service和coupon-user-service三个应用。待所有应用启动完成后,在浏览器打开注册中心页面http://peer1:10000/eureka,可以看到所有服务都显示在页面上了,改造完成后的服务注册表如图5-7所示。

图5-7 改造完成后的服务注册表
注册中心验证通过以后,我们在本地可以通过Postman等工具向coupon-user-service发起服务调用请求,验证改造后的服务是否能正确工作。
如果coupon-user-service在调用其他服务的时候抛出异常,可能是以下几个原因造成的:
(1)传入LoadBalancerClient的Service ID不正确,或者远程方法的访问路径不正确。
(2)对应服务没有注册,检查注册中心页面,查看当前服务状态。
(3)服务已经注册,但注册信息还没被coupon-user-service通过服务发现机制拉取到,等下次服务发现执行后重新发起一次调用即可。