一个最简单的微服务架构 前言 架构图 项目结构 nginx priceservice goodservice gateway 测试 小结

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

架构图

一个最简单的微服务架构
前言
架构图
项目结构
nginx
priceservice
goodservice
gateway
测试
小结

项目结构

一个最简单的微服务架构
前言
架构图
项目结构
nginx
priceservice
goodservice
gateway
测试
小结

这个架构分别由反向代理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,priceservice其中一个,浏览器将会调用失败,因为是单点的。zookeeper挂掉的话,服务还是可以正常的,因为消费者端缓存了服务方列表。在生产环境中,要保证高可用,架构上是不可以出现单点的应用。

小结

   上面的架构只是微服务架构中的一种形态。阿里内部在这方面做得更极致一点,直接将gateway这层去掉,而是作为一个二方包集成到业务应用中,由接入层直接转发流量。简单来说,就是这样的。终端->LVS集群->Aserver集群(nginx的加强版)->应用服务。

git地址:  https://github.com/mycaizilin/microservice