一个最简单的微服务架构
Posted caizl
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一个最简单的微服务架构相关的知识,希望对你有一定的参考价值。
前言
微服务架构一般会有一个开放网关作为总入口,负责分发流量到实际的应用服务上。下面看图。
架构图
项目结构
这个架构分别由反向代理nginx,注册中心zookeeper,开放网关gateway,和两个服务goodservice,priceservice组件而成。为了方便测试,我把建了两个一样的gateway和goodservice。而common作为公共的二方包存在,也是为了简单起见,gateway和service引用同一个二方包。
nginx
nginx除了作为反向代理,也具有负载均衡的功能,默认策略是轮询地址列表。我这里设置8080和8081两个端口,如下
upstream mygateway { server localhost:8080; server localhost:8081; } server { listen 80; server_name localhost; location /route{ proxy_pass http://mygateway; } }
priceservice
非常简单的服务,只提供一个价格查询的接口,如
@Service public class PriceServiceImpl implements PriceService { @Override public Integer getGoodPrice(String name) { return 100; } }
goodservice
也只提供一个接口,但是依赖priceservice,如
@Service public class GoodServiceImpl implements GoodService { private final String SERVICENAME="goodservice"; @Reference(check=false) PriceService priceService; @Override public List<GoodInfo> getGoodList(String name) { List<GoodInfo> goodInfoList=new ArrayList<>(); GoodInfo goodInfo=new GoodInfo(); goodInfo.setName(name); goodInfo.setDescription(SERVICENAME); Integer price= priceService.getGoodPrice(name); goodInfo.setPrice(price); goodInfoList.add(goodInfo); return goodInfoList; } }
gateway
gateway这里的作用是提供一个统一对外的接口,当然还可以加上鉴权,限流,防黑,监控等功能。实现上是通过dubbo的泛化调用将流量通过负载均衡策略转到实际的应用中,均衡策略默认是随机。dubbo的泛化调用是需要去匹配对应接口的方法名和参数类型。正常情况下,是需要通过api注册和管理录入到数据库,再提供给gateway使用的。我这里通过静态块构造一些数据充当api注册。如下
@RestController public class RouteController { private final static List<ServiceModel> serviceModels = new ArrayList<>(); private final static Map<String,GenericService> genericServiceMap=new HashMap<>(); private final static String GATEWAYNAME="gateway"; static { ParameterModel parameterModel = new ParameterModel(); parameterModel.setName("name"); parameterModel.setType("java.lang.String"); List<ParameterModel> parameterModelList = new ArrayList<>(); parameterModelList.add(parameterModel); ServiceModel serviceModel = new ServiceModel(); serviceModel.setApiName("api.service.goodservice"); serviceModel.setServiceName("com.example.demo.common.service.GoodService"); serviceModel.setMethodName("getGoodList"); serviceModel.setParameterModels(parameterModelList); serviceModels.add(serviceModel); } @RequestMapping(value = "/route", method = RequestMethod.GET) public ResultModel execute(@RequestParam String api, @RequestParam String data) { Optional<ServiceModel> serviceModelOptional = serviceModels.stream().filter(x -> x.getApiName().equals(api)).findFirst(); ResultModel resultModel=new ResultModel(); if (!serviceModelOptional.isPresent()) { resultModel.setDescription("api不存在"); } ServiceModel serviceModel=serviceModelOptional.get(); GenericService genericService= genericServiceMap.get(api); if(genericService==null){ ReferenceConfig<GenericService> reference = new ReferenceConfig<>(); reference.setInterface(serviceModel.getServiceName()); reference.setGeneric(true); genericService = reference.get(); genericServiceMap.put(api,genericService); } Object result = genericService.$invoke(serviceModel.getMethodName(),getTypeList(serviceModel).toArray(new String[]{}), dataToValueList(serviceModel,data).toArray()); resultModel.setData(result); resultModel.setDescription(GATEWAYNAME); return resultModel; } /** * 获取参数类型列表 * @param serviceModel * @return */ private List<String> getTypeList(ServiceModel serviceModel) { List<ParameterModel> parameterModelList = serviceModel.getParameterModels(); if (CollectionUtils.isEmpty(parameterModelList)) { return null; } return parameterModelList.stream().map(x -> x.getType()).collect(Collectors.toList()); } /** * 获取data中的值列表 * @param serviceModel * @param data * @return */ private List<Object> dataToValueList(ServiceModel serviceModel, String data) { Map<String, Object> parameterMap = jsonToMap(data); List<ParameterModel> parameterModelList = serviceModel.getParameterModels(); if (CollectionUtils.isEmpty(parameterModelList)) { return null; } List<Object> valueList = new ArrayList<>(); parameterModelList.stream().forEach(x -> { valueList.add(parameterMap.get(x.getName())); }); return valueList; } /** * 将map格式的string转成map对象 * @param json * @return */ public static Map<String, Object> jsonToMap(String json) { ObjectMapper mapper = new ObjectMapper(); try { return mapper.readValue(json, Map.class); } catch (IOException e) { System.out.println(e); } return null; } }
测试
接下来,方便起见,只需要在一台电脑把几个module全部启动起来,在浏览器输入
http://mygateway/route?api=api.service.goodservice&data={"name":"苹果"}
多测试几遍,会看到返回如下
{"data":[{"price":100,"name":"苹果","description":"goodservice","class":"com.example.demo.common.model.GoodInfo"}],"description":"gateway1"} {"data":[{"price":100,"name":"苹果","description":"goodservice1","class":"com.example.demo.common.model.GoodInfo"}],"description":"gateway"} {"data":[{"price":100,"name":"苹果","description":"goodservice","class":"com.example.demo.common.model.GoodInfo"}],"description":"gateway"} {"data":[{"price":100,"name":"苹果","description":"goodservice1","class":"com.example.demo.common.model.GoodInfo"}],"description":"gateway1"}
实际上整个调用链路会在gateway集群和goodservice集群交叉流转,如果这个时候把goodservice或者gateway停掉,浏览器的调用还是会正常返回的,这就是集群的好处。
但是如果这个时候把停掉nginx,zookeeper和priceservice其中一个,浏览器将会调用失败,因为是单点的。在生产环境中,要保证高可用,架构上是不可以出现单点的应用。
小结
上面的架构只是微服务架构中的一种形态。阿里内部在这方面做得更极致一点,直接将gateway这层去掉,而是作为一个二方包集成到业务应用中,由接入层直接转发流量。简单来说,就是这样的。终端->LVS集群->Aserver集群(nginx的加强版)->应用服务。
git地址: https://github.com/mycaizilin/microservice
以上是关于一个最简单的微服务架构的主要内容,如果未能解决你的问题,请参考以下文章
史上最简单的SpringCloud教程 | 第五篇: 路由网关(zuul)