码迷,mamicode.com
首页 > 其他好文 > 详细

一个最简单的微服务架构

时间:2019-04-30 22:02:30      阅读:275      评论:0      收藏:0      [点我收藏+]

标签:div   private   type   cal   功能   需要   odi   启动   print   

前言

      微服务架构一般会有一个开放网关作为总入口,负责分发流量到实际的应用服务上。下面看图。

架构图

技术图片

项目结构

技术图片

这个架构分别由反向代理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

一个最简单的微服务架构

标签:div   private   type   cal   功能   需要   odi   启动   print   

原文地址:https://www.cnblogs.com/caizl/p/10797871.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!