# 通过Java代码调用系统内微应用服务
本章节描述同一个系统内的微应用服务的Java代码调用规范:
- provider:服务提供端在api-module定义好feignClient和相关api接口,并在impl-module模块中实现这些接口,api-module可直接打成jar供其他consumer消费端依赖。
- consumer:服务调用端首先引入服务提供端提供的api-jar,直接使用注解@Autowired把feignClient注入当前应用中使用即可。调用端无需自己实现feignClient以及相关api接口。
# provider服务提供端
api模块pom引入相关依赖,EOS服务自带此依赖,费EOS服务需要引入
<dependency>
<groupId>com.primeton.eos</groupId>
<artifactId>eos-server-starter</artifactId>
</dependency>
启动类是否有@EnableFeignClients和@EnableHystrix注解
参数 | 说明 |
---|---|
@EnableFeignClients | SpringClouds原生注解,扫描和注册Feign客户端Bean定义 |
@EnableHystrix | SpringClouds原生注解,启动熔断降级服务 |
feign.hystrix.enabled | 是否开启hystrix,true:开启, false:不开启 |
FeignClient编写
在api模块创建feignClient接口,示例代码如下:
package com.primeton.eos.feign;
import com.primeton.eos.po.Stock;
import org.springframework.cloud.openfeign.FeignAutoConfiguration;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import static org.springframework.web.bind.annotation.RequestMethod.GET;
import static org.springframework.web.bind.annotation.RequestMethod.POST;
@FeignClient(name = "STOCK-MANAGE", configuration = FeignAutoConfiguration.class, fallback = StockServerFallback.class)
public interface StockServerFeignClient {
@RequestMapping(value = "/api/stock/{versionIds}", method = GET)
Stock[] listStockByProductVersionIds(@PathVariable("versionIds") String versionIds);
@RequestMapping(value = "/api/stock/bulk-delete", method = POST)
void batchStockByVersionIds(@RequestBody Long[] versionIds);
@RequestMapping(value = "/api/stock/{versionId}", method = POST)
void deleteStockByVersionId(@PathVariable("versionId") Long versionId);
}
创建对应的fallback
@Component
public class StockServerFallback implements StockServerFeignClient {
@Override
public Stock[] listStockByProductVersionIds(String versionIds) {
//TODO 这里的fallback用户可根据自身实际业务场景来定具体的fallback逻辑
return null;
}
@Override
public void batchStockByVersionIds(Long[] versionIds) {
//TODO 这里的fallback用户可根据自身实际业务场景来定具体的fallback逻辑
return;
}
@Override
public void deleteStockByVersionId(Long versionId) {
//TODO 这里的fallback用户可根据自身实际业务场景来定具体的fallback逻辑
return;
}
}
# 服务调用端
在服务调用端的pom中引入依赖
在pom文件中引入provider-api模块即可。
在开发中的用法
@Autowired
private StockServerFeignClient feignClient;
public void delete(String[] productIds) {
if (productIds == null) {
return;
}
mapper.deleteBatch(productIds);
versionMapper.deleteBatchByProductIds(productIds);
Map<String,Object> param = new HashMap<>();
List<Long> versionIds = new ArrayList<>();
Arrays.stream(productIds).forEach(productId -> {
param.put("productId",productId);
List<ProductVersionVO> list = versionMapper.listByProductId(param);
getVersionIds(list, versionIds);
});
//系统间不同应用调用:同步删除对应库存信息
feignClient.batchStockByVersionIds(versionIds.toArray(new Long[versionIds.size()]));
}
# 通过Java代码调用其他系统服务
本章节描述不同系统内的微应用服务的Java代码调用规范:
- provider:服务提供端无需做特殊处理,正常提供rest api即可。
- consumer:服务调用端通过restTemplate请求provider所在系统的网关,然后由网关route转发到provider。
# provider相关配置
服务提供者,发布api并授权,获取订阅码
服务提供者通过governor发布到当前系统中的网关上,并授权给服务消费者以及生成服务订阅凭证,服务发布操作如下:
服务订阅授权:
网关配置路由:
# consumer相关配置
订阅凭证和网关path配置
因为服务提供端和服务调用端分属不同的系统,运行期不能直接对服务进行访问(会被拦截,无权访问),需要通过网关路由转发服务调用请求,因此需要配置网关的PATH,供服务调用端发送服务请求,调用服务提供端发布的API。在运行期,网关PATH和订阅凭证可能会变更,因此不宜在代码中写死,需要使用Governor微服务管理平台提供的能力对应用中的网关PATH配置进行动态刷新。要使用Governor的配置能力。
配置完成后,在开发中使用@RefreseScope+@Value注解读取配置即可,如配置有变化,应用会监听配置进行热更新处理。
增加subkey配置类
ApiSubKeyConfig:用来配置apisubkey,跨系统调用api需要在header中填加apisubkey用来给网关验证调用者是否有该api调用的权限,subkey可选择存储在数据库、配置中心、配置文件等等,存储方式可自定义,用法唯一,本示例是根据subkey配置在配置文件中写的(支持Nacos热更新),如来源是其他地方,只需添加对应subkey获取方式即可。
package com.primeton.eos.config;
import com.primeton.eos.dap.sdk.api.webclient.SDKApiSubscriberProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
@RefreshScope
public class ApiSubKeyConfig implements SDKApiSubscriberProvider {
@Value("${provider.gateway.path}")
private String gatewayPath;
@Value("${product-stock.subkey}")
private String subkey;
@Override
public ApiSubscriber getSubscriber(String host, String requestPath) {
if (gatewayPath.indexOf(host) != -1) {
return new ApiSubscriber(subkey);
} else {
return new ApiSubscriber();
}
}
}
使用sdkRestTemplate进行远程调用:
@Service
@RefreshScope
public class OrderServiceImpl {
@Value("${provider.gateway.path}")
private String gatewayPath;
@Value("${product-stock.subkey}")
private String subkey;
@Autowired
@Qualifier("sdkRestTemplate") //sdkRestTemplate
@LoadBalanced
private RestTemplate sdkRestTemplate;
@Override
public OrderVO queryOrderDetail(Long orderId) {
OrderVO vo = new OrderVO();
Order order = orderRepository.findById(orderId).orElse(null);
try {
String versionIds = getVersionIds(order);
//跨系统调用
List<ProductVersionVO> products =
Arrays.asList(sdkRestTemplate.getForObject(gatewayPath + API_PATH_PRODUCT_LIST_PRODUCT_VERSION + "/{versionIds}", ProductVersionVO[].class, versionIds));
vo.setProducts(products);
} catch (Exception e) {
//记录日志
logger.error("Error queryOrderDetail in '{}', error info '{}'", orderId, e.getMessage(), e);
}
BeanUtils.copyProperties(order, vo);
return vo;
}
}
# 非EOS服务调用EOS系统服务
本章节描述非eos服务调用eos系统服务的调用规范;
- provider:eos系统服务可看作是服务提供端。
- consumer:consumer可以是任何客户端,可以是一个java应用,Java微服务应用,非Java应用,前端应用等。consumer通过http请求provider所在系统的网关,然后由网关route转发到provider服务。
此场景配置分三大步骤:
- 创建与consumer对应的订阅系统
- provider配置:发布服务并授权给相应的订阅系统,生成订阅系统的订阅凭证
- consumer配置:拿到步骤2生成的订阅凭证和系统编码,进行配置
# 服务消费者,创建订阅系统
consumer在调用provider提供的服务时,需要携带订阅凭证进行访问,否则在请求网关时会被拒绝访问。订阅凭证的生成分为两步:1.需要先去governor创建订阅系统;2.在provider所在系统针对订阅系统创建订阅者进而生成与订阅系统匹配的订阅凭证,一个订阅凭证只能供一个订阅系统使用,创建订阅系统具体操作如下:
管理平台:
创建系统:
获取系统编码sysKey:
到此,创建完订阅系统之后,再进行provider相关配置。
# provider相关配置
由于是外部系统调用,所以provider的所有提供给外部调用的接口都需要对外发布,供consumer订阅,然后才能进行调用。
服务提供者,发布api并授权,生成订阅凭证
服务提供者通过governor发布到当前系统中的网关上,并授权给服务消费者以及生成服务订阅凭证,服务发布操作如下:
服务订阅授权:分为两步,1.创建订阅者;2.授权
1.创建订阅者:授权系统选择需要被授权的系统即可:即上述创建的订阅系统,创建完订阅者之后,会生成一个订阅凭证
2.授权:授权完成之后把订阅凭证复制下来,这个是订阅系统唯一访问凭证。
网关配置路由:根据发布的api进行路由配置
到此,provider相关配置已经完成,最后进行consumer配置。
# consumer相关配置
完成上述两步骤之后,我们会得到一个和订阅系统匹配的订阅凭证以及订阅系统的系统编码,consumer在发起请求时,将这个订阅凭证和系统编码放进header里面即可,header name参数如下,下面是一段Java示例代码:
参数 | 说明 |
---|---|
X-EOS-ApiSubscriptionKey | HTTP header name,value放我们的订阅凭证即可 |
X-EOS-SourceSysKey | HTTP header name,value放我们的consumer系统编码 |
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.add("X-EOS-ApiSubscriptionKey", "df01292557ef4664967ec470e6cf0bb9");
httpHeaders.add("X-EOS-SourceSysKey", "TESTSYS");
String url = "http://localhost:8081/api/test";
ResponseEntity<String> rspEntity = restTemplate.exchange(url, HttpMethod.GET, new HttpEntity<>(null, httpHeaders), String.class);